Dell commited on
Commit
9e19a70
·
1 Parent(s): b084b88
.history/CatVTON/model/pipeline_20260615122430.py ADDED
@@ -0,0 +1,332 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import inspect
2
+ import os
3
+ from typing import Union
4
+
5
+ import PIL
6
+ import numpy as np
7
+ import torch
8
+ import tqdm
9
+ from accelerate import load_checkpoint_in_model
10
+ from diffusers import AutoencoderKL, DDIMScheduler, UNet2DConditionModel
11
+ from diffusers.pipelines.stable_diffusion.safety_checker import \
12
+ StableDiffusionSafetyChecker
13
+ from diffusers.utils.torch_utils import randn_tensor
14
+ from huggingface_hub import snapshot_download
15
+ from transformers import CLIPImageProcessor
16
+
17
+ from model.attn_processor import SkipAttnProcessor
18
+ from model.utils import get_trainable_module, init_adapter
19
+ from utils import (compute_vae_encodings, numpy_to_pil, prepare_image,
20
+ prepare_mask_image, resize_and_crop, resize_and_padding)
21
+
22
+
23
+ class CatVTONPipeline:
24
+ def __init__(
25
+ self,
26
+ base_ckpt,
27
+ attn_ckpt,
28
+ attn_ckpt_version="mix",
29
+ weight_dtype=torch.float32,
30
+ device='cuda',
31
+ compile=False,
32
+ skip_safety_check=False,
33
+ use_tf32=True,
34
+ ):
35
+ self.device = device
36
+ self.weight_dtype = weight_dtype
37
+ self.skip_safety_check = skip_safety_check
38
+
39
+ self.noise_scheduler = DDIMScheduler.from_pretrained(base_ckpt, subfolder="scheduler")
40
+ self.vae = AutoencoderKL.from_pretrained("stabilityai/sd-vae-ft-mse").to(device, dtype=weight_dtype)
41
+ if not skip_safety_check:
42
+ self.feature_extractor = CLIPImageProcessor.from_pretrained(base_ckpt, subfolder="feature_extractor")
43
+ self.safety_checker = StableDiffusionSafetyChecker.from_pretrained(base_ckpt, subfolder="safety_checker").to(device, dtype=weight_dtype)
44
+ self.unet = UNet2DConditionModel.from_pretrained(base_ckpt, subfolder="unet").to(device, dtype=weight_dtype)
45
+ init_adapter(self.unet, cross_attn_cls=SkipAttnProcessor) # Skip Cross-Attention
46
+ self.attn_modules = get_trainable_module(self.unet, "attention")
47
+ self.auto_attn_ckpt_load(attn_ckpt, attn_ckpt_version)
48
+ # Pytorch 2.0 Compile
49
+ if compile:
50
+ self.unet = torch.compile(self.unet)
51
+ self.vae = torch.compile(self.vae, mode="reduce-overhead")
52
+
53
+ # Enable TF32 for faster training on Ampere GPUs (A100 and RTX 30 series).
54
+ if use_tf32:
55
+ torch.set_float32_matmul_precision("high")
56
+ torch.backends.cuda.matmul.allow_tf32 = True
57
+
58
+ def auto_attn_ckpt_load(self, attn_ckpt, version):
59
+ sub_folder = {
60
+ "mix": "mix-48k-1024",
61
+ "vitonhd": "vitonhd-16k-512",
62
+ "dresscode": "dresscode-16k-512",
63
+ }[version]
64
+ if os.path.exists(attn_ckpt):
65
+ load_checkpoint_in_model(self.attn_modules, os.path.join(attn_ckpt, sub_folder, 'attention'))
66
+ else:
67
+ repo_path = snapshot_download(repo_id=attn_ckpt)
68
+ print(f"Downloaded {attn_ckpt} to {repo_path}")
69
+ load_checkpoint_in_model(self.attn_modules, os.path.join(repo_path, sub_folder, 'attention'))
70
+
71
+ def run_safety_checker(self, image):
72
+ if self.safety_checker is None:
73
+ has_nsfw_concept = None
74
+ else:
75
+ safety_checker_input = self.feature_extractor(image, return_tensors="pt").to(self.device)
76
+ image, has_nsfw_concept = self.safety_checker(
77
+ images=image, clip_input=safety_checker_input.pixel_values.to(self.weight_dtype)
78
+ )
79
+ return image, has_nsfw_concept
80
+
81
+ def check_inputs(self, image, condition_image, mask, width, height):
82
+ if isinstance(image, torch.Tensor) and isinstance(condition_image, torch.Tensor) and isinstance(mask, torch.Tensor):
83
+ return image, condition_image, mask
84
+ assert image.size == mask.size, "Image and mask must have the same size"
85
+ image = resize_and_crop(image, (width, height))
86
+ mask = resize_and_crop(mask, (width, height))
87
+ condition_image = resize_and_padding(condition_image, (width, height))
88
+ return image, condition_image, mask
89
+
90
+ def prepare_extra_step_kwargs(self, generator, eta):
91
+ # prepare extra kwargs for the scheduler step, since not all schedulers have the same signature
92
+ # eta (η) is only used with the DDIMScheduler, it will be ignored for other schedulers.
93
+ # eta corresponds to η in DDIM paper: https://arxiv.org/abs/2010.02502
94
+ # and should be between [0, 1]
95
+
96
+ accepts_eta = "eta" in set(
97
+ inspect.signature(self.noise_scheduler.step).parameters.keys()
98
+ )
99
+ extra_step_kwargs = {}
100
+ if accepts_eta:
101
+ extra_step_kwargs["eta"] = eta
102
+
103
+ # check if the scheduler accepts generator
104
+ accepts_generator = "generator" in set(
105
+ inspect.signature(self.noise_scheduler.step).parameters.keys()
106
+ )
107
+ if accepts_generator:
108
+ extra_step_kwargs["generator"] = generator
109
+ return extra_step_kwargs
110
+
111
+ @torch.no_grad()
112
+ def __call__(
113
+ self,
114
+ image: Union[PIL.Image.Image, torch.Tensor],
115
+ condition_image: Union[PIL.Image.Image, torch.Tensor],
116
+ mask: Union[PIL.Image.Image, torch.Tensor],
117
+ num_inference_steps: int = 50,
118
+ guidance_scale: float = 2.5,
119
+ height: int = 1024,
120
+ width: int = 768,
121
+ generator=None,
122
+ eta=1.0,
123
+ **kwargs
124
+ ):
125
+ concat_dim = -2 # FIXME: y axis concat
126
+ # Prepare inputs to Tensor
127
+ image, condition_image, mask = self.check_inputs(image, condition_image, mask, width, height)
128
+ image = prepare_image(image).to(self.device, dtype=self.weight_dtype)
129
+ condition_image = prepare_image(condition_image).to(self.device, dtype=self.weight_dtype)
130
+ mask = prepare_mask_image(mask).to(self.device, dtype=self.weight_dtype)
131
+ # Mask image
132
+ masked_image = image * (mask < 0.5)
133
+ # VAE encoding
134
+ masked_latent = compute_vae_encodings(masked_image, self.vae)
135
+ condition_latent = compute_vae_encodings(condition_image, self.vae)
136
+ mask_latent = torch.nn.functional.interpolate(mask, size=masked_latent.shape[-2:], mode="nearest")
137
+ del image, mask, condition_image
138
+ # Concatenate latents
139
+ masked_latent_concat = torch.cat([masked_latent, condition_latent], dim=concat_dim)
140
+ mask_latent_concat = torch.cat([mask_latent, torch.zeros_like(mask_latent)], dim=concat_dim)
141
+ # Prepare noise
142
+ latents = randn_tensor(
143
+ masked_latent_concat.shape,
144
+ generator=generator,
145
+ device=masked_latent_concat.device,
146
+ dtype=self.weight_dtype,
147
+ )
148
+ # Prepare timesteps
149
+ self.noise_scheduler.set_timesteps(num_inference_steps, device=self.device)
150
+ timesteps = self.noise_scheduler.timesteps
151
+ latents = latents * self.noise_scheduler.init_noise_sigma
152
+ # Classifier-Free Guidance
153
+ if do_classifier_free_guidance := (guidance_scale > 1.0):
154
+ masked_latent_concat = torch.cat(
155
+ [
156
+ torch.cat([masked_latent, torch.zeros_like(condition_latent)], dim=concat_dim),
157
+ masked_latent_concat,
158
+ ]
159
+ )
160
+ mask_latent_concat = torch.cat([mask_latent_concat] * 2)
161
+
162
+ # Denoising loop
163
+ extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
164
+ num_warmup_steps = (len(timesteps) - num_inference_steps * self.noise_scheduler.order)
165
+ with tqdm.tqdm(total=num_inference_steps) as progress_bar:
166
+ for i, t in enumerate(timesteps):
167
+ # expand the latents if we are doing classifier free guidance
168
+ non_inpainting_latent_model_input = (torch.cat([latents] * 2) if do_classifier_free_guidance else latents)
169
+ non_inpainting_latent_model_input = self.noise_scheduler.scale_model_input(non_inpainting_latent_model_input, t)
170
+ # prepare the input for the inpainting model
171
+ inpainting_latent_model_input = torch.cat([non_inpainting_latent_model_input, mask_latent_concat, masked_latent_concat], dim=1)
172
+ # predict the noise residual
173
+ noise_pred= self.unet(
174
+ inpainting_latent_model_input,
175
+ t.to(self.device),
176
+ encoder_hidden_states=None, # FIXME
177
+ return_dict=False,
178
+ )[0]
179
+ # perform guidance
180
+ if do_classifier_free_guidance:
181
+ noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
182
+ noise_pred = noise_pred_uncond + guidance_scale * (
183
+ noise_pred_text - noise_pred_uncond
184
+ )
185
+ # compute the previous noisy sample x_t -> x_t-1
186
+ latents = self.noise_scheduler.step(
187
+ noise_pred, t, latents, **extra_step_kwargs
188
+ ).prev_sample
189
+ # call the callback, if provided
190
+ if i == len(timesteps) - 1 or (
191
+ (i + 1) > num_warmup_steps
192
+ and (i + 1) % self.noise_scheduler.order == 0
193
+ ):
194
+ progress_bar.update()
195
+
196
+ # Decode the final latents
197
+ latents = latents.split(latents.shape[concat_dim] // 2, dim=concat_dim)[0]
198
+ latents = 1 / self.vae.config.scaling_factor * latents
199
+ image = self.vae.decode(latents.to(self.device, dtype=self.weight_dtype)).sample
200
+ image = (image / 2 + 0.5).clamp(0, 1)
201
+ # we always cast to float32 as this does not cause significant overhead and is compatible with bfloat16
202
+ image = image.cpu().permute(0, 2, 3, 1).float().numpy()
203
+ image = numpy_to_pil(image)
204
+
205
+ # Safety Check
206
+ if not self.skip_safety_check:
207
+ current_script_directory = os.path.dirname(os.path.realpath(__file__))
208
+ nsfw_image = os.path.join(os.path.dirname(current_script_directory), 'resource', 'img', 'NSFW.jpg')
209
+ nsfw_image = PIL.Image.open(nsfw_image).resize(image[0].size)
210
+ image_np = np.array(image)
211
+ _, has_nsfw_concept = self.run_safety_checker(image=image_np)
212
+ for i, not_safe in enumerate(has_nsfw_concept):
213
+ if not_safe:
214
+ image[i] = nsfw_image
215
+ return image
216
+
217
+
218
+ class CatVTONPix2PixPipeline(CatVTONPipeline):
219
+ def auto_attn_ckpt_load(self, attn_ckpt, version):
220
+ # TODO: Temperal fix for the model version
221
+ if os.path.exists(attn_ckpt):
222
+ load_checkpoint_in_model(self.attn_modules, os.path.join(attn_ckpt, version, 'attention'))
223
+ else:
224
+ repo_path = snapshot_download(repo_id=attn_ckpt)
225
+ print(f"Downloaded {attn_ckpt} to {repo_path}")
226
+ load_checkpoint_in_model(self.attn_modules, os.path.join(repo_path, version, 'attention'))
227
+
228
+ def check_inputs(self, image, condition_image, width, height):
229
+ if isinstance(image, torch.Tensor) and isinstance(condition_image, torch.Tensor) and isinstance(torch.Tensor):
230
+ return image, condition_image
231
+ image = resize_and_crop(image, (width, height))
232
+ condition_image = resize_and_padding(condition_image, (width, height))
233
+ return image, condition_image
234
+
235
+ @torch.no_grad()
236
+ def __call__(
237
+ self,
238
+ image: Union[PIL.Image.Image, torch.Tensor],
239
+ condition_image: Union[PIL.Image.Image, torch.Tensor],
240
+ num_inference_steps: int = 50,
241
+ guidance_scale: float = 2.5,
242
+ height: int = 1024,
243
+ width: int = 768,
244
+ generator=None,
245
+ eta=1.0,
246
+ **kwargs
247
+ ):
248
+ concat_dim = -1
249
+ # Prepare inputs to Tensor
250
+ image, condition_image = self.check_inputs(image, condition_image, width, height)
251
+ image = prepare_image(image).to(self.device, dtype=self.weight_dtype)
252
+ condition_image = prepare_image(condition_image).to(self.device, dtype=self.weight_dtype)
253
+ # VAE encoding
254
+ image_latent = compute_vae_encodings(image, self.vae)
255
+ condition_latent = compute_vae_encodings(condition_image, self.vae)
256
+ del image, condition_image
257
+ # Concatenate latents
258
+ condition_latent_concat = torch.cat([image_latent, condition_latent], dim=concat_dim)
259
+ # Prepare noise
260
+ latents = randn_tensor(
261
+ condition_latent_concat.shape,
262
+ generator=generator,
263
+ device=condition_latent_concat.device,
264
+ dtype=self.weight_dtype,
265
+ )
266
+ # Prepare timesteps
267
+ self.noise_scheduler.set_timesteps(num_inference_steps, device=self.device)
268
+ timesteps = self.noise_scheduler.timesteps
269
+ latents = latents * self.noise_scheduler.init_noise_sigma
270
+ # Classifier-Free Guidance
271
+ if do_classifier_free_guidance := (guidance_scale > 1.0):
272
+ condition_latent_concat = torch.cat(
273
+ [
274
+ torch.cat([image_latent, torch.zeros_like(condition_latent)], dim=concat_dim),
275
+ condition_latent_concat,
276
+ ]
277
+ )
278
+
279
+ # Denoising loop
280
+ extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
281
+ num_warmup_steps = (len(timesteps) - num_inference_steps * self.noise_scheduler.order)
282
+ with tqdm.tqdm(total=num_inference_steps) as progress_bar:
283
+ for i, t in enumerate(timesteps):
284
+ # expand the latents if we are doing classifier free guidance
285
+ latent_model_input = (torch.cat([latents] * 2) if do_classifier_free_guidance else latents)
286
+ latent_model_input = self.noise_scheduler.scale_model_input(latent_model_input, t)
287
+ # prepare the input for the inpainting model
288
+ p2p_latent_model_input = torch.cat([latent_model_input, condition_latent_concat], dim=1)
289
+ # predict the noise residual
290
+ noise_pred= self.unet(
291
+ p2p_latent_model_input,
292
+ t.to(self.device),
293
+ encoder_hidden_states=None,
294
+ return_dict=False,
295
+ )[0]
296
+ # perform guidance
297
+ if do_classifier_free_guidance:
298
+ noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
299
+ noise_pred = noise_pred_uncond + guidance_scale * (
300
+ noise_pred_text - noise_pred_uncond
301
+ )
302
+ # compute the previous noisy sample x_t -> x_t-1
303
+ latents = self.noise_scheduler.step(
304
+ noise_pred, t, latents, **extra_step_kwargs
305
+ ).prev_sample
306
+ # call the callback, if provided
307
+ if i == len(timesteps) - 1 or (
308
+ (i + 1) > num_warmup_steps
309
+ and (i + 1) % self.noise_scheduler.order == 0
310
+ ):
311
+ progress_bar.update()
312
+
313
+ # Decode the final latents
314
+ latents = latents.split(latents.shape[concat_dim] // 2, dim=concat_dim)[0]
315
+ latents = 1 / self.vae.config.scaling_factor * latents
316
+ image = self.vae.decode(latents.to(self.device, dtype=self.weight_dtype)).sample
317
+ image = (image / 2 + 0.5).clamp(0, 1)
318
+ # we always cast to float32 as this does not cause significant overhead and is compatible with bfloat16
319
+ image = image.cpu().permute(0, 2, 3, 1).float().numpy()
320
+ image = numpy_to_pil(image)
321
+
322
+ # Safety Check
323
+ if not self.skip_safety_check:
324
+ current_script_directory = os.path.dirname(os.path.realpath(__file__))
325
+ nsfw_image = os.path.join(os.path.dirname(current_script_directory), 'resource', 'img', 'NSFW.jpg')
326
+ nsfw_image = PIL.Image.open(nsfw_image).resize(image[0].size)
327
+ image_np = np.array(image)
328
+ _, has_nsfw_concept = self.run_safety_checker(image=image_np)
329
+ for i, not_safe in enumerate(has_nsfw_concept):
330
+ if not_safe:
331
+ image[i] = nsfw_image
332
+ return image
.history/CatVTON/model/pipeline_20260618144306.py ADDED
@@ -0,0 +1,341 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import inspect
2
+ import os
3
+ from typing import Union
4
+
5
+ import PIL
6
+ import numpy as np
7
+ import torch
8
+ import tqdm
9
+ from accelerate import load_checkpoint_in_model
10
+ from diffusers import AutoencoderKL, DDIMScheduler, UNet2DConditionModel
11
+ from diffusers.pipelines.stable_diffusion.safety_checker import \
12
+ StableDiffusionSafetyChecker
13
+ from diffusers.utils.torch_utils import randn_tensor
14
+ from huggingface_hub import snapshot_download
15
+ from transformers import CLIPImageProcessor
16
+
17
+ from model.attn_processor import SkipAttnProcessor
18
+ from model.utils import get_trainable_module, init_adapter
19
+ from utils import (compute_vae_encodings, numpy_to_pil, prepare_image,
20
+ prepare_mask_image, resize_and_crop, resize_and_padding)
21
+
22
+
23
+ class CatVTONPipeline:
24
+ def __init__(
25
+ self,
26
+ base_ckpt,
27
+ attn_ckpt,
28
+ attn_ckpt_version="mix",
29
+ weight_dtype=torch.float32,
30
+ device='cuda',
31
+ compile=False,
32
+ skip_safety_check=False,
33
+ use_tf32=True,
34
+ ):
35
+ self.device = device
36
+ self.weight_dtype = weight_dtype
37
+ self.skip_safety_check = skip_safety_check
38
+
39
+ self.noise_scheduler = DDIMScheduler.from_pretrained(base_ckpt, subfolder="scheduler")
40
+ self.vae = AutoencoderKL.from_pretrained("stabilityai/sd-vae-ft-mse").to(device, dtype=weight_dtype)
41
+ if not skip_safety_check:
42
+ self.feature_extractor = CLIPImageProcessor.from_pretrained(base_ckpt, subfolder="feature_extractor")
43
+ self.safety_checker = StableDiffusionSafetyChecker.from_pretrained(base_ckpt, subfolder="safety_checker").to(device, dtype=weight_dtype)
44
+ self.unet = UNet2DConditionModel.from_pretrained(base_ckpt, subfolder="unet").to(device, dtype=weight_dtype)
45
+ init_adapter(self.unet, cross_attn_cls=SkipAttnProcessor) # Skip Cross-Attention
46
+ self.attn_modules = get_trainable_module(self.unet, "attention")
47
+ self.auto_attn_ckpt_load(attn_ckpt, attn_ckpt_version)
48
+ # Pytorch 2.0 Compile
49
+ if compile:
50
+ self.unet = torch.compile(self.unet)
51
+ self.vae = torch.compile(self.vae, mode="reduce-overhead")
52
+
53
+ # Enable TF32 for faster training on Ampere GPUs (A100 and RTX 30 series).
54
+ if use_tf32:
55
+ torch.set_float32_matmul_precision("high")
56
+ torch.backends.cuda.matmul.allow_tf32 = True
57
+
58
+ def auto_attn_ckpt_load(self, attn_ckpt, version):
59
+ sub_folder = {
60
+ "mix": "mix-48k-1024",
61
+ "vitonhd": "vitonhd-16k-512",
62
+ "dresscode": "dresscode-16k-512",
63
+ }[version]
64
+ if os.path.exists(attn_ckpt):
65
+ load_checkpoint_in_model(self.attn_modules, os.path.join(attn_ckpt, sub_folder, 'attention'))
66
+ else:
67
+ repo_path = snapshot_download(repo_id=attn_ckpt)
68
+ print(f"Downloaded {attn_ckpt} to {repo_path}")
69
+ load_checkpoint_in_model(self.attn_modules, os.path.join(repo_path, sub_folder, 'attention'))
70
+
71
+ def run_safety_checker(self, image):
72
+ if self.safety_checker is None:
73
+ has_nsfw_concept = None
74
+ else:
75
+ safety_checker_input = self.feature_extractor(image, return_tensors="pt").to(self.device)
76
+ image, has_nsfw_concept = self.safety_checker(
77
+ images=image, clip_input=safety_checker_input.pixel_values.to(self.weight_dtype)
78
+ )
79
+ return image, has_nsfw_concept
80
+
81
+ def check_inputs(self, image, condition_image, mask, width, height):
82
+ if isinstance(image, torch.Tensor) and isinstance(condition_image, torch.Tensor) and isinstance(mask, torch.Tensor):
83
+ return image, condition_image, mask
84
+ assert image.size == mask.size, "Image and mask must have the same size"
85
+ image = resize_and_crop(image, (width, height))
86
+ mask = resize_and_crop(mask, (width, height))
87
+ condition_image = resize_and_padding(condition_image, (width, height))
88
+ return image, condition_image, mask
89
+
90
+ def prepare_extra_step_kwargs(self, generator, eta):
91
+ # prepare extra kwargs for the scheduler step, since not all schedulers have the same signature
92
+ # eta (η) is only used with the DDIMScheduler, it will be ignored for other schedulers.
93
+ # eta corresponds to η in DDIM paper: https://arxiv.org/abs/2010.02502
94
+ # and should be between [0, 1]
95
+
96
+ accepts_eta = "eta" in set(
97
+ inspect.signature(self.noise_scheduler.step).parameters.keys()
98
+ )
99
+ extra_step_kwargs = {}
100
+ if accepts_eta:
101
+ extra_step_kwargs["eta"] = eta
102
+
103
+ # check if the scheduler accepts generator
104
+ accepts_generator = "generator" in set(
105
+ inspect.signature(self.noise_scheduler.step).parameters.keys()
106
+ )
107
+ if accepts_generator:
108
+ extra_step_kwargs["generator"] = generator
109
+ return extra_step_kwargs
110
+
111
+ @torch.no_grad()
112
+ def __call__(
113
+ self,
114
+ image: Union[PIL.Image.Image, torch.Tensor],
115
+ condition_image: Union[PIL.Image.Image, torch.Tensor],
116
+ mask: Union[PIL.Image.Image, torch.Tensor],
117
+ num_inference_steps: int = 50,
118
+ guidance_scale: float = 2.5,
119
+ height: int = 1024,
120
+ width: int = 768,
121
+ generator=None,
122
+ eta=1.0,
123
+ **kwargs
124
+ ):
125
+ concat_dim = -2 # FIXME: y axis concat
126
+ # Prepare inputs to Tensor
127
+ image, condition_image, mask = self.check_inputs(image, condition_image, mask, width, height)
128
+ image = prepare_image(image).to(self.device, dtype=self.weight_dtype)
129
+ condition_image = prepare_image(condition_image).to(self.device, dtype=self.weight_dtype)
130
+ mask = prepare_mask_image(mask).to(self.device, dtype=self.weight_dtype)
131
+ # Mask image
132
+ masked_image = image * (mask < 0.5)
133
+ # VAE encoding
134
+ masked_latent = compute_vae_encodings(masked_image, self.vae)
135
+ condition_latent = compute_vae_encodings(condition_image, self.vae)
136
+ mask_latent = torch.nn.functional.interpolate(mask, size=masked_latent.shape[-2:], mode="nearest")
137
+ del image, mask, condition_image
138
+ # Concatenate latents
139
+ masked_latent_concat = torch.cat([masked_latent, condition_latent], dim=concat_dim)
140
+ mask_latent_concat = torch.cat([mask_latent, torch.zeros_like(mask_latent)], dim=concat_dim)
141
+ # Prepare noise
142
+ latents = randn_tensor(
143
+ masked_latent_concat.shape,
144
+ generator=generator,
145
+ device=masked_latent_concat.device,
146
+ dtype=self.weight_dtype,
147
+ )
148
+ # Prepare timesteps
149
+ self.noise_scheduler.set_timesteps(num_inference_steps, device=self.device)
150
+ timesteps = self.noise_scheduler.timesteps
151
+ latents = latents * self.noise_scheduler.init_noise_sigma
152
+ # Classifier-Free Guidance
153
+ if do_classifier_free_guidance := (guidance_scale > 1.0):
154
+ masked_latent_concat = torch.cat(
155
+ [
156
+ torch.cat([masked_latent, torch.zeros_like(condition_latent)], dim=concat_dim),
157
+ masked_latent_concat,
158
+ ]
159
+ )
160
+ mask_latent_concat = torch.cat([mask_latent_concat] * 2)
161
+
162
+ # Denoising loop
163
+ extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
164
+ num_warmup_steps = (len(timesteps) - num_inference_steps * self.noise_scheduler.order)
165
+ with tqdm.tqdm(total=num_inference_steps) as progress_bar:
166
+ for i, t in enumerate(timesteps):
167
+ # expand the latents if we are doing classifier free guidance
168
+ non_inpainting_latent_model_input = (torch.cat([latents] * 2) if do_classifier_free_guidance else latents)
169
+ non_inpainting_latent_model_input = self.noise_scheduler.scale_model_input(non_inpainting_latent_model_input, t)
170
+ # prepare the input for the inpainting model
171
+ inpainting_latent_model_input = torch.cat([non_inpainting_latent_model_input, mask_latent_concat, masked_latent_concat], dim=1)
172
+ # predict the noise residual
173
+ noise_pred= self.unet(
174
+ inpainting_latent_model_input,
175
+ t.to(self.device),
176
+ encoder_hidden_states=None, # FIXME
177
+ return_dict=False,
178
+ )[0]
179
+ # perform guidance
180
+ if do_classifier_free_guidance:
181
+ noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
182
+ noise_pred = noise_pred_uncond + guidance_scale * (
183
+ noise_pred_text - noise_pred_uncond
184
+ )
185
+ # compute the previous noisy sample x_t -> x_t-1
186
+ latents = self.noise_scheduler.step(
187
+ noise_pred, t, latents, **extra_step_kwargs
188
+ ).prev_sample
189
+ # call the callback, if provided
190
+ if i == len(timesteps) - 1 or (
191
+ (i + 1) > num_warmup_steps
192
+ and (i + 1) % self.noise_scheduler.order == 0
193
+ ):
194
+ progress_bar.update()
195
+
196
+ # Decode the final latents
197
+ latents = latents.split(latents.shape[concat_dim] // 2, dim=concat_dim)[0]
198
+ latents = 1 / self.vae.config.scaling_factor * latents
199
+ image = self.vae.decode(latents.to(self.device, dtype=self.weight_dtype)).sample
200
+ image = (image / 2 + 0.5).clamp(0, 1)
201
+ # we always cast to float32 as this does not cause significant overhead and is compatible with bfloat16
202
+ image = image.cpu().permute(0, 2, 3, 1).float().numpy()
203
+ image = numpy_to_pil(image)
204
+
205
+ # Safety Check
206
+ if not self.skip_safety_check:
207
+ current_script_directory = os.path.dirname(os.path.realpath(__file__))
208
+ nsfw_image_path = os.path.join(
209
+ os.path.dirname(current_script_directory), "resource", "img", "NSFW.jpg"
210
+ )
211
+
212
+ image_np = np.array(image)
213
+ _, has_nsfw_concept = self.run_safety_checker(image=image_np)
214
+
215
+ # Deployed HF Spaces may not include the placeholder NSFW image.
216
+ # If missing, skip replacement but still return the generated result.
217
+ nsfw_image = None
218
+ if os.path.exists(nsfw_image_path):
219
+ nsfw_image = PIL.Image.open(nsfw_image_path).resize(image[0].size)
220
+
221
+ for i, not_safe in enumerate(has_nsfw_concept):
222
+ if not_safe and nsfw_image is not None:
223
+ image[i] = nsfw_image
224
+ return image
225
+
226
+
227
+ class CatVTONPix2PixPipeline
228
+ def auto_attn_ckpt_load(self, attn_ckpt, version):
229
+ # TODO: Temperal fix for the model version
230
+ if os.path.exists(attn_ckpt):
231
+ load_checkpoint_in_model(self.attn_modules, os.path.join(attn_ckpt, version, 'attention'))
232
+ else:
233
+ repo_path = snapshot_download(repo_id=attn_ckpt)
234
+ print(f"Downloaded {attn_ckpt} to {repo_path}")
235
+ load_checkpoint_in_model(self.attn_modules, os.path.join(repo_path, version, 'attention'))
236
+
237
+ def check_inputs(self, image, condition_image, width, height):
238
+ if isinstance(image, torch.Tensor) and isinstance(condition_image, torch.Tensor) and isinstance(torch.Tensor):
239
+ return image, condition_image
240
+ image = resize_and_crop(image, (width, height))
241
+ condition_image = resize_and_padding(condition_image, (width, height))
242
+ return image, condition_image
243
+
244
+ @torch.no_grad()
245
+ def __call__(
246
+ self,
247
+ image: Union[PIL.Image.Image, torch.Tensor],
248
+ condition_image: Union[PIL.Image.Image, torch.Tensor],
249
+ num_inference_steps: int = 50,
250
+ guidance_scale: float = 2.5,
251
+ height: int = 1024,
252
+ width: int = 768,
253
+ generator=None,
254
+ eta=1.0,
255
+ **kwargs
256
+ ):
257
+ concat_dim = -1
258
+ # Prepare inputs to Tensor
259
+ image, condition_image = self.check_inputs(image, condition_image, width, height)
260
+ image = prepare_image(image).to(self.device, dtype=self.weight_dtype)
261
+ condition_image = prepare_image(condition_image).to(self.device, dtype=self.weight_dtype)
262
+ # VAE encoding
263
+ image_latent = compute_vae_encodings(image, self.vae)
264
+ condition_latent = compute_vae_encodings(condition_image, self.vae)
265
+ del image, condition_image
266
+ # Concatenate latents
267
+ condition_latent_concat = torch.cat([image_latent, condition_latent], dim=concat_dim)
268
+ # Prepare noise
269
+ latents = randn_tensor(
270
+ condition_latent_concat.shape,
271
+ generator=generator,
272
+ device=condition_latent_concat.device,
273
+ dtype=self.weight_dtype,
274
+ )
275
+ # Prepare timesteps
276
+ self.noise_scheduler.set_timesteps(num_inference_steps, device=self.device)
277
+ timesteps = self.noise_scheduler.timesteps
278
+ latents = latents * self.noise_scheduler.init_noise_sigma
279
+ # Classifier-Free Guidance
280
+ if do_classifier_free_guidance := (guidance_scale > 1.0):
281
+ condition_latent_concat = torch.cat(
282
+ [
283
+ torch.cat([image_latent, torch.zeros_like(condition_latent)], dim=concat_dim),
284
+ condition_latent_concat,
285
+ ]
286
+ )
287
+
288
+ # Denoising loop
289
+ extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
290
+ num_warmup_steps = (len(timesteps) - num_inference_steps * self.noise_scheduler.order)
291
+ with tqdm.tqdm(total=num_inference_steps) as progress_bar:
292
+ for i, t in enumerate(timesteps):
293
+ # expand the latents if we are doing classifier free guidance
294
+ latent_model_input = (torch.cat([latents] * 2) if do_classifier_free_guidance else latents)
295
+ latent_model_input = self.noise_scheduler.scale_model_input(latent_model_input, t)
296
+ # prepare the input for the inpainting model
297
+ p2p_latent_model_input = torch.cat([latent_model_input, condition_latent_concat], dim=1)
298
+ # predict the noise residual
299
+ noise_pred= self.unet(
300
+ p2p_latent_model_input,
301
+ t.to(self.device),
302
+ encoder_hidden_states=None,
303
+ return_dict=False,
304
+ )[0]
305
+ # perform guidance
306
+ if do_classifier_free_guidance:
307
+ noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
308
+ noise_pred = noise_pred_uncond + guidance_scale * (
309
+ noise_pred_text - noise_pred_uncond
310
+ )
311
+ # compute the previous noisy sample x_t -> x_t-1
312
+ latents = self.noise_scheduler.step(
313
+ noise_pred, t, latents, **extra_step_kwargs
314
+ ).prev_sample
315
+ # call the callback, if provided
316
+ if i == len(timesteps) - 1 or (
317
+ (i + 1) > num_warmup_steps
318
+ and (i + 1) % self.noise_scheduler.order == 0
319
+ ):
320
+ progress_bar.update()
321
+
322
+ # Decode the final latents
323
+ latents = latents.split(latents.shape[concat_dim] // 2, dim=concat_dim)[0]
324
+ latents = 1 / self.vae.config.scaling_factor * latents
325
+ image = self.vae.decode(latents.to(self.device, dtype=self.weight_dtype)).sample
326
+ image = (image / 2 + 0.5).clamp(0, 1)
327
+ # we always cast to float32 as this does not cause significant overhead and is compatible with bfloat16
328
+ image = image.cpu().permute(0, 2, 3, 1).float().numpy()
329
+ image = numpy_to_pil(image)
330
+
331
+ # Safety Check
332
+ if not self.skip_safety_check:
333
+ current_script_directory = os.path.dirname(os.path.realpath(__file__))
334
+ nsfw_image = os.path.join(os.path.dirname(current_script_directory), 'resource', 'img', 'NSFW.jpg')
335
+ nsfw_image = PIL.Image.open(nsfw_image).resize(image[0].size)
336
+ image_np = np.array(image)
337
+ _, has_nsfw_concept = self.run_safety_checker(image=image_np)
338
+ for i, not_safe in enumerate(has_nsfw_concept):
339
+ if not_safe:
340
+ image[i] = nsfw_image
341
+ return image
.history/CatVTON/model/pipeline_20260618144328.py ADDED
@@ -0,0 +1,341 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import inspect
2
+ import os
3
+ from typing import Union
4
+
5
+ import PIL
6
+ import numpy as np
7
+ import torch
8
+ import tqdm
9
+ from accelerate import load_checkpoint_in_model
10
+ from diffusers import AutoencoderKL, DDIMScheduler, UNet2DConditionModel
11
+ from diffusers.pipelines.stable_diffusion.safety_checker import \
12
+ StableDiffusionSafetyChecker
13
+ from diffusers.utils.torch_utils import randn_tensor
14
+ from huggingface_hub import snapshot_download
15
+ from transformers import CLIPImageProcessor
16
+
17
+ from model.attn_processor import SkipAttnProcessor
18
+ from model.utils import get_trainable_module, init_adapter
19
+ from utils import (compute_vae_encodings, numpy_to_pil, prepare_image,
20
+ prepare_mask_image, resize_and_crop, resize_and_padding)
21
+
22
+
23
+ class CatVTONPipeline:
24
+ def __init__(
25
+ self,
26
+ base_ckpt,
27
+ attn_ckpt,
28
+ attn_ckpt_version="mix",
29
+ weight_dtype=torch.float32,
30
+ device='cuda',
31
+ compile=False,
32
+ skip_safety_check=False,
33
+ use_tf32=True,
34
+ ):
35
+ self.device = device
36
+ self.weight_dtype = weight_dtype
37
+ self.skip_safety_check = skip_safety_check
38
+
39
+ self.noise_scheduler = DDIMScheduler.from_pretrained(base_ckpt, subfolder="scheduler")
40
+ self.vae = AutoencoderKL.from_pretrained("stabilityai/sd-vae-ft-mse").to(device, dtype=weight_dtype)
41
+ if not skip_safety_check:
42
+ self.feature_extractor = CLIPImageProcessor.from_pretrained(base_ckpt, subfolder="feature_extractor")
43
+ self.safety_checker = StableDiffusionSafetyChecker.from_pretrained(base_ckpt, subfolder="safety_checker").to(device, dtype=weight_dtype)
44
+ self.unet = UNet2DConditionModel.from_pretrained(base_ckpt, subfolder="unet").to(device, dtype=weight_dtype)
45
+ init_adapter(self.unet, cross_attn_cls=SkipAttnProcessor) # Skip Cross-Attention
46
+ self.attn_modules = get_trainable_module(self.unet, "attention")
47
+ self.auto_attn_ckpt_load(attn_ckpt, attn_ckpt_version)
48
+ # Pytorch 2.0 Compile
49
+ if compile:
50
+ self.unet = torch.compile(self.unet)
51
+ self.vae = torch.compile(self.vae, mode="reduce-overhead")
52
+
53
+ # Enable TF32 for faster training on Ampere GPUs (A100 and RTX 30 series).
54
+ if use_tf32:
55
+ torch.set_float32_matmul_precision("high")
56
+ torch.backends.cuda.matmul.allow_tf32 = True
57
+
58
+ def auto_attn_ckpt_load(self, attn_ckpt, version):
59
+ sub_folder = {
60
+ "mix": "mix-48k-1024",
61
+ "vitonhd": "vitonhd-16k-512",
62
+ "dresscode": "dresscode-16k-512",
63
+ }[version]
64
+ if os.path.exists(attn_ckpt):
65
+ load_checkpoint_in_model(self.attn_modules, os.path.join(attn_ckpt, sub_folder, 'attention'))
66
+ else:
67
+ repo_path = snapshot_download(repo_id=attn_ckpt)
68
+ print(f"Downloaded {attn_ckpt} to {repo_path}")
69
+ load_checkpoint_in_model(self.attn_modules, os.path.join(repo_path, sub_folder, 'attention'))
70
+
71
+ def run_safety_checker(self, image):
72
+ if self.safety_checker is None:
73
+ has_nsfw_concept = None
74
+ else:
75
+ safety_checker_input = self.feature_extractor(image, return_tensors="pt").to(self.device)
76
+ image, has_nsfw_concept = self.safety_checker(
77
+ images=image, clip_input=safety_checker_input.pixel_values.to(self.weight_dtype)
78
+ )
79
+ return image, has_nsfw_concept
80
+
81
+ def check_inputs(self, image, condition_image, mask, width, height):
82
+ if isinstance(image, torch.Tensor) and isinstance(condition_image, torch.Tensor) and isinstance(mask, torch.Tensor):
83
+ return image, condition_image, mask
84
+ assert image.size == mask.size, "Image and mask must have the same size"
85
+ image = resize_and_crop(image, (width, height))
86
+ mask = resize_and_crop(mask, (width, height))
87
+ condition_image = resize_and_padding(condition_image, (width, height))
88
+ return image, condition_image, mask
89
+
90
+ def prepare_extra_step_kwargs(self, generator, eta):
91
+ # prepare extra kwargs for the scheduler step, since not all schedulers have the same signature
92
+ # eta (η) is only used with the DDIMScheduler, it will be ignored for other schedulers.
93
+ # eta corresponds to η in DDIM paper: https://arxiv.org/abs/2010.02502
94
+ # and should be between [0, 1]
95
+
96
+ accepts_eta = "eta" in set(
97
+ inspect.signature(self.noise_scheduler.step).parameters.keys()
98
+ )
99
+ extra_step_kwargs = {}
100
+ if accepts_eta:
101
+ extra_step_kwargs["eta"] = eta
102
+
103
+ # check if the scheduler accepts generator
104
+ accepts_generator = "generator" in set(
105
+ inspect.signature(self.noise_scheduler.step).parameters.keys()
106
+ )
107
+ if accepts_generator:
108
+ extra_step_kwargs["generator"] = generator
109
+ return extra_step_kwargs
110
+
111
+ @torch.no_grad()
112
+ def __call__(
113
+ self,
114
+ image: Union[PIL.Image.Image, torch.Tensor],
115
+ condition_image: Union[PIL.Image.Image, torch.Tensor],
116
+ mask: Union[PIL.Image.Image, torch.Tensor],
117
+ num_inference_steps: int = 50,
118
+ guidance_scale: float = 2.5,
119
+ height: int = 1024,
120
+ width: int = 768,
121
+ generator=None,
122
+ eta=1.0,
123
+ **kwargs
124
+ ):
125
+ concat_dim = -2 # FIXME: y axis concat
126
+ # Prepare inputs to Tensor
127
+ image, condition_image, mask = self.check_inputs(image, condition_image, mask, width, height)
128
+ image = prepare_image(image).to(self.device, dtype=self.weight_dtype)
129
+ condition_image = prepare_image(condition_image).to(self.device, dtype=self.weight_dtype)
130
+ mask = prepare_mask_image(mask).to(self.device, dtype=self.weight_dtype)
131
+ # Mask image
132
+ masked_image = image * (mask < 0.5)
133
+ # VAE encoding
134
+ masked_latent = compute_vae_encodings(masked_image, self.vae)
135
+ condition_latent = compute_vae_encodings(condition_image, self.vae)
136
+ mask_latent = torch.nn.functional.interpolate(mask, size=masked_latent.shape[-2:], mode="nearest")
137
+ del image, mask, condition_image
138
+ # Concatenate latents
139
+ masked_latent_concat = torch.cat([masked_latent, condition_latent], dim=concat_dim)
140
+ mask_latent_concat = torch.cat([mask_latent, torch.zeros_like(mask_latent)], dim=concat_dim)
141
+ # Prepare noise
142
+ latents = randn_tensor(
143
+ masked_latent_concat.shape,
144
+ generator=generator,
145
+ device=masked_latent_concat.device,
146
+ dtype=self.weight_dtype,
147
+ )
148
+ # Prepare timesteps
149
+ self.noise_scheduler.set_timesteps(num_inference_steps, device=self.device)
150
+ timesteps = self.noise_scheduler.timesteps
151
+ latents = latents * self.noise_scheduler.init_noise_sigma
152
+ # Classifier-Free Guidance
153
+ if do_classifier_free_guidance := (guidance_scale > 1.0):
154
+ masked_latent_concat = torch.cat(
155
+ [
156
+ torch.cat([masked_latent, torch.zeros_like(condition_latent)], dim=concat_dim),
157
+ masked_latent_concat,
158
+ ]
159
+ )
160
+ mask_latent_concat = torch.cat([mask_latent_concat] * 2)
161
+
162
+ # Denoising loop
163
+ extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
164
+ num_warmup_steps = (len(timesteps) - num_inference_steps * self.noise_scheduler.order)
165
+ with tqdm.tqdm(total=num_inference_steps) as progress_bar:
166
+ for i, t in enumerate(timesteps):
167
+ # expand the latents if we are doing classifier free guidance
168
+ non_inpainting_latent_model_input = (torch.cat([latents] * 2) if do_classifier_free_guidance else latents)
169
+ non_inpainting_latent_model_input = self.noise_scheduler.scale_model_input(non_inpainting_latent_model_input, t)
170
+ # prepare the input for the inpainting model
171
+ inpainting_latent_model_input = torch.cat([non_inpainting_latent_model_input, mask_latent_concat, masked_latent_concat], dim=1)
172
+ # predict the noise residual
173
+ noise_pred= self.unet(
174
+ inpainting_latent_model_input,
175
+ t.to(self.device),
176
+ encoder_hidden_states=None, # FIXME
177
+ return_dict=False,
178
+ )[0]
179
+ # perform guidance
180
+ if do_classifier_free_guidance:
181
+ noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
182
+ noise_pred = noise_pred_uncond + guidance_scale * (
183
+ noise_pred_text - noise_pred_uncond
184
+ )
185
+ # compute the previous noisy sample x_t -> x_t-1
186
+ latents = self.noise_scheduler.step(
187
+ noise_pred, t, latents, **extra_step_kwargs
188
+ ).prev_sample
189
+ # call the callback, if provided
190
+ if i == len(timesteps) - 1 or (
191
+ (i + 1) > num_warmup_steps
192
+ and (i + 1) % self.noise_scheduler.order == 0
193
+ ):
194
+ progress_bar.update()
195
+
196
+ # Decode the final latents
197
+ latents = latents.split(latents.shape[concat_dim] // 2, dim=concat_dim)[0]
198
+ latents = 1 / self.vae.config.scaling_factor * latents
199
+ image = self.vae.decode(latents.to(self.device, dtype=self.weight_dtype)).sample
200
+ image = (image / 2 + 0.5).clamp(0, 1)
201
+ # we always cast to float32 as this does not cause significant overhead and is compatible with bfloat16
202
+ image = image.cpu().permute(0, 2, 3, 1).float().numpy()
203
+ image = numpy_to_pil(image)
204
+
205
+ # Safety Check
206
+ if not self.skip_safety_check:
207
+ current_script_directory = os.path.dirname(os.path.realpath(__file__))
208
+ nsfw_image_path = os.path.join(
209
+ os.path.dirname(current_script_directory), "resource", "img", "NSFW.jpg"
210
+ )
211
+
212
+ image_np = np.array(image)
213
+ _, has_nsfw_concept = self.run_safety_checker(image=image_np)
214
+
215
+ # Deployed HF Spaces may not include the placeholder NSFW image.
216
+ # If missing, skip replacement but still return the generated result.
217
+ nsfw_image = None
218
+ if os.path.exists(nsfw_image_path):
219
+ nsfw_image = PIL.Image.open(nsfw_image_path).resize(image[0].size)
220
+
221
+ for i, not_safe in enumerate(has_nsfw_concept):
222
+ if not_safe and nsfw_image is not None:
223
+ image[i] = nsfw_image
224
+ return image
225
+
226
+
227
+ class CatVTONPix2PixPipeline(CatVTONPipeline):
228
+ def auto_attn_ckpt_load
229
+ # TODO: Temperal fix for the model version
230
+ if os.path.exists(attn_ckpt):
231
+ load_checkpoint_in_model(self.attn_modules, os.path.join(attn_ckpt, version, 'attention'))
232
+ else:
233
+ repo_path = snapshot_download(repo_id=attn_ckpt)
234
+ print(f"Downloaded {attn_ckpt} to {repo_path}")
235
+ load_checkpoint_in_model(self.attn_modules, os.path.join(repo_path, version, 'attention'))
236
+
237
+ def check_inputs(self, image, condition_image, width, height):
238
+ if isinstance(image, torch.Tensor) and isinstance(condition_image, torch.Tensor) and isinstance(torch.Tensor):
239
+ return image, condition_image
240
+ image = resize_and_crop(image, (width, height))
241
+ condition_image = resize_and_padding(condition_image, (width, height))
242
+ return image, condition_image
243
+
244
+ @torch.no_grad()
245
+ def __call__(
246
+ self,
247
+ image: Union[PIL.Image.Image, torch.Tensor],
248
+ condition_image: Union[PIL.Image.Image, torch.Tensor],
249
+ num_inference_steps: int = 50,
250
+ guidance_scale: float = 2.5,
251
+ height: int = 1024,
252
+ width: int = 768,
253
+ generator=None,
254
+ eta=1.0,
255
+ **kwargs
256
+ ):
257
+ concat_dim = -1
258
+ # Prepare inputs to Tensor
259
+ image, condition_image = self.check_inputs(image, condition_image, width, height)
260
+ image = prepare_image(image).to(self.device, dtype=self.weight_dtype)
261
+ condition_image = prepare_image(condition_image).to(self.device, dtype=self.weight_dtype)
262
+ # VAE encoding
263
+ image_latent = compute_vae_encodings(image, self.vae)
264
+ condition_latent = compute_vae_encodings(condition_image, self.vae)
265
+ del image, condition_image
266
+ # Concatenate latents
267
+ condition_latent_concat = torch.cat([image_latent, condition_latent], dim=concat_dim)
268
+ # Prepare noise
269
+ latents = randn_tensor(
270
+ condition_latent_concat.shape,
271
+ generator=generator,
272
+ device=condition_latent_concat.device,
273
+ dtype=self.weight_dtype,
274
+ )
275
+ # Prepare timesteps
276
+ self.noise_scheduler.set_timesteps(num_inference_steps, device=self.device)
277
+ timesteps = self.noise_scheduler.timesteps
278
+ latents = latents * self.noise_scheduler.init_noise_sigma
279
+ # Classifier-Free Guidance
280
+ if do_classifier_free_guidance := (guidance_scale > 1.0):
281
+ condition_latent_concat = torch.cat(
282
+ [
283
+ torch.cat([image_latent, torch.zeros_like(condition_latent)], dim=concat_dim),
284
+ condition_latent_concat,
285
+ ]
286
+ )
287
+
288
+ # Denoising loop
289
+ extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
290
+ num_warmup_steps = (len(timesteps) - num_inference_steps * self.noise_scheduler.order)
291
+ with tqdm.tqdm(total=num_inference_steps) as progress_bar:
292
+ for i, t in enumerate(timesteps):
293
+ # expand the latents if we are doing classifier free guidance
294
+ latent_model_input = (torch.cat([latents] * 2) if do_classifier_free_guidance else latents)
295
+ latent_model_input = self.noise_scheduler.scale_model_input(latent_model_input, t)
296
+ # prepare the input for the inpainting model
297
+ p2p_latent_model_input = torch.cat([latent_model_input, condition_latent_concat], dim=1)
298
+ # predict the noise residual
299
+ noise_pred= self.unet(
300
+ p2p_latent_model_input,
301
+ t.to(self.device),
302
+ encoder_hidden_states=None,
303
+ return_dict=False,
304
+ )[0]
305
+ # perform guidance
306
+ if do_classifier_free_guidance:
307
+ noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
308
+ noise_pred = noise_pred_uncond + guidance_scale * (
309
+ noise_pred_text - noise_pred_uncond
310
+ )
311
+ # compute the previous noisy sample x_t -> x_t-1
312
+ latents = self.noise_scheduler.step(
313
+ noise_pred, t, latents, **extra_step_kwargs
314
+ ).prev_sample
315
+ # call the callback, if provided
316
+ if i == len(timesteps) - 1 or (
317
+ (i + 1) > num_warmup_steps
318
+ and (i + 1) % self.noise_scheduler.order == 0
319
+ ):
320
+ progress_bar.update()
321
+
322
+ # Decode the final latents
323
+ latents = latents.split(latents.shape[concat_dim] // 2, dim=concat_dim)[0]
324
+ latents = 1 / self.vae.config.scaling_factor * latents
325
+ image = self.vae.decode(latents.to(self.device, dtype=self.weight_dtype)).sample
326
+ image = (image / 2 + 0.5).clamp(0, 1)
327
+ # we always cast to float32 as this does not cause significant overhead and is compatible with bfloat16
328
+ image = image.cpu().permute(0, 2, 3, 1).float().numpy()
329
+ image = numpy_to_pil(image)
330
+
331
+ # Safety Check
332
+ if not self.skip_safety_check:
333
+ current_script_directory = os.path.dirname(os.path.realpath(__file__))
334
+ nsfw_image = os.path.join(os.path.dirname(current_script_directory), 'resource', 'img', 'NSFW.jpg')
335
+ nsfw_image = PIL.Image.open(nsfw_image).resize(image[0].size)
336
+ image_np = np.array(image)
337
+ _, has_nsfw_concept = self.run_safety_checker(image=image_np)
338
+ for i, not_safe in enumerate(has_nsfw_concept):
339
+ if not_safe:
340
+ image[i] = nsfw_image
341
+ return image
.history/CatVTON/model/pipeline_20260618144342.py ADDED
@@ -0,0 +1,341 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import inspect
2
+ import os
3
+ from typing import Union
4
+
5
+ import PIL
6
+ import numpy as np
7
+ import torch
8
+ import tqdm
9
+ from accelerate import load_checkpoint_in_model
10
+ from diffusers import AutoencoderKL, DDIMScheduler, UNet2DConditionModel
11
+ from diffusers.pipelines.stable_diffusion.safety_checker import \
12
+ StableDiffusionSafetyChecker
13
+ from diffusers.utils.torch_utils import randn_tensor
14
+ from huggingface_hub import snapshot_download
15
+ from transformers import CLIPImageProcessor
16
+
17
+ from model.attn_processor import SkipAttnProcessor
18
+ from model.utils import get_trainable_module, init_adapter
19
+ from utils import (compute_vae_encodings, numpy_to_pil, prepare_image,
20
+ prepare_mask_image, resize_and_crop, resize_and_padding)
21
+
22
+
23
+ class CatVTONPipeline:
24
+ def __init__(
25
+ self,
26
+ base_ckpt,
27
+ attn_ckpt,
28
+ attn_ckpt_version="mix",
29
+ weight_dtype=torch.float32,
30
+ device='cuda',
31
+ compile=False,
32
+ skip_safety_check=False,
33
+ use_tf32=True,
34
+ ):
35
+ self.device = device
36
+ self.weight_dtype = weight_dtype
37
+ self.skip_safety_check = skip_safety_check
38
+
39
+ self.noise_scheduler = DDIMScheduler.from_pretrained(base_ckpt, subfolder="scheduler")
40
+ self.vae = AutoencoderKL.from_pretrained("stabilityai/sd-vae-ft-mse").to(device, dtype=weight_dtype)
41
+ if not skip_safety_check:
42
+ self.feature_extractor = CLIPImageProcessor.from_pretrained(base_ckpt, subfolder="feature_extractor")
43
+ self.safety_checker = StableDiffusionSafetyChecker.from_pretrained(base_ckpt, subfolder="safety_checker").to(device, dtype=weight_dtype)
44
+ self.unet = UNet2DConditionModel.from_pretrained(base_ckpt, subfolder="unet").to(device, dtype=weight_dtype)
45
+ init_adapter(self.unet, cross_attn_cls=SkipAttnProcessor) # Skip Cross-Attention
46
+ self.attn_modules = get_trainable_module(self.unet, "attention")
47
+ self.auto_attn_ckpt_load(attn_ckpt, attn_ckpt_version)
48
+ # Pytorch 2.0 Compile
49
+ if compile:
50
+ self.unet = torch.compile(self.unet)
51
+ self.vae = torch.compile(self.vae, mode="reduce-overhead")
52
+
53
+ # Enable TF32 for faster training on Ampere GPUs (A100 and RTX 30 series).
54
+ if use_tf32:
55
+ torch.set_float32_matmul_precision("high")
56
+ torch.backends.cuda.matmul.allow_tf32 = True
57
+
58
+ def auto_attn_ckpt_load(self, attn_ckpt, version):
59
+ sub_folder = {
60
+ "mix": "mix-48k-1024",
61
+ "vitonhd": "vitonhd-16k-512",
62
+ "dresscode": "dresscode-16k-512",
63
+ }[version]
64
+ if os.path.exists(attn_ckpt):
65
+ load_checkpoint_in_model(self.attn_modules, os.path.join(attn_ckpt, sub_folder, 'attention'))
66
+ else:
67
+ repo_path = snapshot_download(repo_id=attn_ckpt)
68
+ print(f"Downloaded {attn_ckpt} to {repo_path}")
69
+ load_checkpoint_in_model(self.attn_modules, os.path.join(repo_path, sub_folder, 'attention'))
70
+
71
+ def run_safety_checker(self, image):
72
+ if self.safety_checker is None:
73
+ has_nsfw_concept = None
74
+ else:
75
+ safety_checker_input = self.feature_extractor(image, return_tensors="pt").to(self.device)
76
+ image, has_nsfw_concept = self.safety_checker(
77
+ images=image, clip_input=safety_checker_input.pixel_values.to(self.weight_dtype)
78
+ )
79
+ return image, has_nsfw_concept
80
+
81
+ def check_inputs(self, image, condition_image, mask, width, height):
82
+ if isinstance(image, torch.Tensor) and isinstance(condition_image, torch.Tensor) and isinstance(mask, torch.Tensor):
83
+ return image, condition_image, mask
84
+ assert image.size == mask.size, "Image and mask must have the same size"
85
+ image = resize_and_crop(image, (width, height))
86
+ mask = resize_and_crop(mask, (width, height))
87
+ condition_image = resize_and_padding(condition_image, (width, height))
88
+ return image, condition_image, mask
89
+
90
+ def prepare_extra_step_kwargs(self, generator, eta):
91
+ # prepare extra kwargs for the scheduler step, since not all schedulers have the same signature
92
+ # eta (η) is only used with the DDIMScheduler, it will be ignored for other schedulers.
93
+ # eta corresponds to η in DDIM paper: https://arxiv.org/abs/2010.02502
94
+ # and should be between [0, 1]
95
+
96
+ accepts_eta = "eta" in set(
97
+ inspect.signature(self.noise_scheduler.step).parameters.keys()
98
+ )
99
+ extra_step_kwargs = {}
100
+ if accepts_eta:
101
+ extra_step_kwargs["eta"] = eta
102
+
103
+ # check if the scheduler accepts generator
104
+ accepts_generator = "generator" in set(
105
+ inspect.signature(self.noise_scheduler.step).parameters.keys()
106
+ )
107
+ if accepts_generator:
108
+ extra_step_kwargs["generator"] = generator
109
+ return extra_step_kwargs
110
+
111
+ @torch.no_grad()
112
+ def __call__(
113
+ self,
114
+ image: Union[PIL.Image.Image, torch.Tensor],
115
+ condition_image: Union[PIL.Image.Image, torch.Tensor],
116
+ mask: Union[PIL.Image.Image, torch.Tensor],
117
+ num_inference_steps: int = 50,
118
+ guidance_scale: float = 2.5,
119
+ height: int = 1024,
120
+ width: int = 768,
121
+ generator=None,
122
+ eta=1.0,
123
+ **kwargs
124
+ ):
125
+ concat_dim = -2 # FIXME: y axis concat
126
+ # Prepare inputs to Tensor
127
+ image, condition_image, mask = self.check_inputs(image, condition_image, mask, width, height)
128
+ image = prepare_image(image).to(self.device, dtype=self.weight_dtype)
129
+ condition_image = prepare_image(condition_image).to(self.device, dtype=self.weight_dtype)
130
+ mask = prepare_mask_image(mask).to(self.device, dtype=self.weight_dtype)
131
+ # Mask image
132
+ masked_image = image * (mask < 0.5)
133
+ # VAE encoding
134
+ masked_latent = compute_vae_encodings(masked_image, self.vae)
135
+ condition_latent = compute_vae_encodings(condition_image, self.vae)
136
+ mask_latent = torch.nn.functional.interpolate(mask, size=masked_latent.shape[-2:], mode="nearest")
137
+ del image, mask, condition_image
138
+ # Concatenate latents
139
+ masked_latent_concat = torch.cat([masked_latent, condition_latent], dim=concat_dim)
140
+ mask_latent_concat = torch.cat([mask_latent, torch.zeros_like(mask_latent)], dim=concat_dim)
141
+ # Prepare noise
142
+ latents = randn_tensor(
143
+ masked_latent_concat.shape,
144
+ generator=generator,
145
+ device=masked_latent_concat.device,
146
+ dtype=self.weight_dtype,
147
+ )
148
+ # Prepare timesteps
149
+ self.noise_scheduler.set_timesteps(num_inference_steps, device=self.device)
150
+ timesteps = self.noise_scheduler.timesteps
151
+ latents = latents * self.noise_scheduler.init_noise_sigma
152
+ # Classifier-Free Guidance
153
+ if do_classifier_free_guidance := (guidance_scale > 1.0):
154
+ masked_latent_concat = torch.cat(
155
+ [
156
+ torch.cat([masked_latent, torch.zeros_like(condition_latent)], dim=concat_dim),
157
+ masked_latent_concat,
158
+ ]
159
+ )
160
+ mask_latent_concat = torch.cat([mask_latent_concat] * 2)
161
+
162
+ # Denoising loop
163
+ extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
164
+ num_warmup_steps = (len(timesteps) - num_inference_steps * self.noise_scheduler.order)
165
+ with tqdm.tqdm(total=num_inference_steps) as progress_bar:
166
+ for i, t in enumerate(timesteps):
167
+ # expand the latents if we are doing classifier free guidance
168
+ non_inpainting_latent_model_input = (torch.cat([latents] * 2) if do_classifier_free_guidance else latents)
169
+ non_inpainting_latent_model_input = self.noise_scheduler.scale_model_input(non_inpainting_latent_model_input, t)
170
+ # prepare the input for the inpainting model
171
+ inpainting_latent_model_input = torch.cat([non_inpainting_latent_model_input, mask_latent_concat, masked_latent_concat], dim=1)
172
+ # predict the noise residual
173
+ noise_pred= self.unet(
174
+ inpainting_latent_model_input,
175
+ t.to(self.device),
176
+ encoder_hidden_states=None, # FIXME
177
+ return_dict=False,
178
+ )[0]
179
+ # perform guidance
180
+ if do_classifier_free_guidance:
181
+ noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
182
+ noise_pred = noise_pred_uncond + guidance_scale * (
183
+ noise_pred_text - noise_pred_uncond
184
+ )
185
+ # compute the previous noisy sample x_t -> x_t-1
186
+ latents = self.noise_scheduler.step(
187
+ noise_pred, t, latents, **extra_step_kwargs
188
+ ).prev_sample
189
+ # call the callback, if provided
190
+ if i == len(timesteps) - 1 or (
191
+ (i + 1) > num_warmup_steps
192
+ and (i + 1) % self.noise_scheduler.order == 0
193
+ ):
194
+ progress_bar.update()
195
+
196
+ # Decode the final latents
197
+ latents = latents.split(latents.shape[concat_dim] // 2, dim=concat_dim)[0]
198
+ latents = 1 / self.vae.config.scaling_factor * latents
199
+ image = self.vae.decode(latents.to(self.device, dtype=self.weight_dtype)).sample
200
+ image = (image / 2 + 0.5).clamp(0, 1)
201
+ # we always cast to float32 as this does not cause significant overhead and is compatible with bfloat16
202
+ image = image.cpu().permute(0, 2, 3, 1).float().numpy()
203
+ image = numpy_to_pil(image)
204
+
205
+ # Safety Check
206
+ if not self.skip_safety_check:
207
+ current_script_directory = os.path.dirname(os.path.realpath(__file__))
208
+ nsfw_image_path = os.path.join(
209
+ os.path.dirname(current_script_directory), "resource", "img", "NSFW.jpg"
210
+ )
211
+
212
+ image_np = np.array(image)
213
+ _, has_nsfw_concept = self.run_safety_checker(image=image_np)
214
+
215
+ # Deployed HF Spaces may not include the placeholder NSFW image.
216
+ # If missing, skip replacement but still return the generated result.
217
+ nsfw_image = None
218
+ if os.path.exists(nsfw_image_path):
219
+ nsfw_image = PIL.Image.open(nsfw_image_path).resize(image[0].size)
220
+
221
+ for i, not_safe in enumerate(has_nsfw_concept):
222
+ if not_safe and nsfw_image is not None:
223
+ image[i] = nsfw_image
224
+ return image
225
+
226
+
227
+ class CatVTONPix2PixPipeline(CatVTONPipeline):
228
+ def auto_attn_ckpt_load
229
+ # TODO: Temperal fix for the model version
230
+ if os.path.exists(attn_ckpt):
231
+ load_checkpoint_in_model(self.attn_modules, os.path.join(attn_ckpt, version, 'attention'))
232
+ else:
233
+ repo_path = snapshot_download(repo_id=attn_ckpt)
234
+ print(f"Downloaded {attn_ckpt} to {repo_path}")
235
+ load_checkpoint_in_model(self.attn_modules, os.path.join(repo_path, version, 'attention'))
236
+
237
+ def check_inputs(self, image, condition_image, width, height):
238
+ if isinstance(image, torch.Tensor) and isinstance(condition_image, torch.Tensor) and isinstance(torch.Tensor):
239
+ return image, condition_image
240
+ image = resize_and_crop(image, (width, height))
241
+ condition_image = resize_and_padding(condition_image, (width, height))
242
+ return image, condition_image
243
+
244
+ @torch.no_grad()
245
+ def __call__(
246
+ self,
247
+ image: Union[PIL.Image.Image, torch.Tensor],
248
+ condition_image: Union[PIL.Image.Image, torch.Tensor],
249
+ num_inference_steps: int = 50,
250
+ guidance_scale: float = 2.5,
251
+ height: int = 1024,
252
+ width: int = 768,
253
+ generator=None,
254
+ eta=1.0,
255
+ **kwargs
256
+ ):
257
+ concat_dim = -1
258
+ # Prepare inputs to Tensor
259
+ image, condition_image = self.check_inputs(image, condition_image, width, height)
260
+ image = prepare_image(image).to(self.device, dtype=self.weight_dtype)
261
+ condition_image = prepare_image(condition_image).to(self.device, dtype=self.weight_dtype)
262
+ # VAE encoding
263
+ image_latent = compute_vae_encodings(image, self.vae)
264
+ condition_latent = compute_vae_encodings(condition_image, self.vae)
265
+ del image, condition_image
266
+ # Concatenate latents
267
+ condition_latent_concat = torch.cat([image_latent, condition_latent], dim=concat_dim)
268
+ # Prepare noise
269
+ latents = randn_tensor(
270
+ condition_latent_concat.shape,
271
+ generator=generator,
272
+ device=condition_latent_concat.device,
273
+ dtype=self.weight_dtype,
274
+ )
275
+ # Prepare timesteps
276
+ self.noise_scheduler.set_timesteps(num_inference_steps, device=self.device)
277
+ timesteps = self.noise_scheduler.timesteps
278
+ latents = latents * self.noise_scheduler.init_noise_sigma
279
+ # Classifier-Free Guidance
280
+ if do_classifier_free_guidance := (guidance_scale > 1.0):
281
+ condition_latent_concat = torch.cat(
282
+ [
283
+ torch.cat([image_latent, torch.zeros_like(condition_latent)], dim=concat_dim),
284
+ condition_latent_concat,
285
+ ]
286
+ )
287
+
288
+ # Denoising loop
289
+ extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
290
+ num_warmup_steps = (len(timesteps) - num_inference_steps * self.noise_scheduler.order)
291
+ with tqdm.tqdm(total=num_inference_steps) as progress_bar:
292
+ for i, t in enumerate(timesteps):
293
+ # expand the latents if we are doing classifier free guidance
294
+ latent_model_input = (torch.cat([latents] * 2) if do_classifier_free_guidance else latents)
295
+ latent_model_input = self.noise_scheduler.scale_model_input(latent_model_input, t)
296
+ # prepare the input for the inpainting model
297
+ p2p_latent_model_input = torch.cat([latent_model_input, condition_latent_concat], dim=1)
298
+ # predict the noise residual
299
+ noise_pred= self.unet(
300
+ p2p_latent_model_input,
301
+ t.to(self.device),
302
+ encoder_hidden_states=None,
303
+ return_dict=False,
304
+ )[0]
305
+ # perform guidance
306
+ if do_classifier_free_guidance:
307
+ noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
308
+ noise_pred = noise_pred_uncond + guidance_scale * (
309
+ noise_pred_text - noise_pred_uncond
310
+ )
311
+ # compute the previous noisy sample x_t -> x_t-1
312
+ latents = self.noise_scheduler.step(
313
+ noise_pred, t, latents, **extra_step_kwargs
314
+ ).prev_sample
315
+ # call the callback, if provided
316
+ if i == len(timesteps) - 1 or (
317
+ (i + 1) > num_warmup_steps
318
+ and (i + 1) % self.noise_scheduler.order == 0
319
+ ):
320
+ progress_bar.update()
321
+
322
+ # Decode the final latents
323
+ latents = latents.split(latents.shape[concat_dim] // 2, dim=concat_dim)[0]
324
+ latents = 1 / self.vae.config.scaling_factor * latents
325
+ image = self.vae.decode(latents.to(self.device, dtype=self.weight_dtype)).sample
326
+ image = (image / 2 + 0.5).clamp(0, 1)
327
+ # we always cast to float32 as this does not cause significant overhead and is compatible with bfloat16
328
+ image = image.cpu().permute(0, 2, 3, 1).float().numpy()
329
+ image = numpy_to_pil(image)
330
+
331
+ # Safety Check
332
+ if not self.skip_safety_check:
333
+ current_script_directory = os.path.dirname(os.path.realpath(__file__))
334
+ nsfw_image = os.path.join(os.path.dirname(current_script_directory), 'resource', 'img', 'NSFW.jpg')
335
+ nsfw_image = PIL.Image.open(nsfw_image).resize(image[0].size)
336
+ image_np = np.array(image)
337
+ _, has_nsfw_concept = self.run_safety_checker(image=image_np)
338
+ for i, not_safe in enumerate(has_nsfw_concept):
339
+ if not_safe:
340
+ image[i] = nsfw_image
341
+ return image
.history/CatVTON/model/pipeline_20260618144429.py ADDED
@@ -0,0 +1,341 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import inspect
2
+ import os
3
+ from typing import Union
4
+
5
+ import PIL
6
+ import numpy as np
7
+ import torch
8
+ import tqdm
9
+ from accelerate import load_checkpoint_in_model
10
+ from diffusers import AutoencoderKL, DDIMScheduler, UNet2DConditionModel
11
+ from diffusers.pipelines.stable_diffusion.safety_checker import \
12
+ StableDiffusionSafetyChecker
13
+ from diffusers.utils.torch_utils import randn_tensor
14
+ from huggingface_hub import snapshot_download
15
+ from transformers import CLIPImageProcessor
16
+
17
+ from model.attn_processor import SkipAttnProcessor
18
+ from model.utils import get_trainable_module, init_adapter
19
+ from utils import (compute_vae_encodings, numpy_to_pil, prepare_image,
20
+ prepare_mask_image, resize_and_crop, resize_and_padding)
21
+
22
+
23
+ class CatVTONPipeline:
24
+ def __init__(
25
+ self,
26
+ base_ckpt,
27
+ attn_ckpt,
28
+ attn_ckpt_version="mix",
29
+ weight_dtype=torch.float32,
30
+ device='cuda',
31
+ compile=False,
32
+ skip_safety_check=False,
33
+ use_tf32=True,
34
+ ):
35
+ self.device = device
36
+ self.weight_dtype = weight_dtype
37
+ self.skip_safety_check = skip_safety_check
38
+
39
+ self.noise_scheduler = DDIMScheduler.from_pretrained(base_ckpt, subfolder="scheduler")
40
+ self.vae = AutoencoderKL.from_pretrained("stabilityai/sd-vae-ft-mse").to(device, dtype=weight_dtype)
41
+ if not skip_safety_check:
42
+ self.feature_extractor = CLIPImageProcessor.from_pretrained(base_ckpt, subfolder="feature_extractor")
43
+ self.safety_checker = StableDiffusionSafetyChecker.from_pretrained(base_ckpt, subfolder="safety_checker").to(device, dtype=weight_dtype)
44
+ self.unet = UNet2DConditionModel.from_pretrained(base_ckpt, subfolder="unet").to(device, dtype=weight_dtype)
45
+ init_adapter(self.unet, cross_attn_cls=SkipAttnProcessor) # Skip Cross-Attention
46
+ self.attn_modules = get_trainable_module(self.unet, "attention")
47
+ self.auto_attn_ckpt_load(attn_ckpt, attn_ckpt_version)
48
+ # Pytorch 2.0 Compile
49
+ if compile:
50
+ self.unet = torch.compile(self.unet)
51
+ self.vae = torch.compile(self.vae, mode="reduce-overhead")
52
+
53
+ # Enable TF32 for faster training on Ampere GPUs (A100 and RTX 30 series).
54
+ if use_tf32:
55
+ torch.set_float32_matmul_precision("high")
56
+ torch.backends.cuda.matmul.allow_tf32 = True
57
+
58
+ def auto_attn_ckpt_load(self, attn_ckpt, version):
59
+ sub_folder = {
60
+ "mix": "mix-48k-1024",
61
+ "vitonhd": "vitonhd-16k-512",
62
+ "dresscode": "dresscode-16k-512",
63
+ }[version]
64
+ if os.path.exists(attn_ckpt):
65
+ load_checkpoint_in_model(self.attn_modules, os.path.join(attn_ckpt, sub_folder, 'attention'))
66
+ else:
67
+ repo_path = snapshot_download(repo_id=attn_ckpt)
68
+ print(f"Downloaded {attn_ckpt} to {repo_path}")
69
+ load_checkpoint_in_model(self.attn_modules, os.path.join(repo_path, sub_folder, 'attention'))
70
+
71
+ def run_safety_checker(self, image):
72
+ if self.safety_checker is None:
73
+ has_nsfw_concept = None
74
+ else:
75
+ safety_checker_input = self.feature_extractor(image, return_tensors="pt").to(self.device)
76
+ image, has_nsfw_concept = self.safety_checker(
77
+ images=image, clip_input=safety_checker_input.pixel_values.to(self.weight_dtype)
78
+ )
79
+ return image, has_nsfw_concept
80
+
81
+ def check_inputs(self, image, condition_image, mask, width, height):
82
+ if isinstance(image, torch.Tensor) and isinstance(condition_image, torch.Tensor) and isinstance(mask, torch.Tensor):
83
+ return image, condition_image, mask
84
+ assert image.size == mask.size, "Image and mask must have the same size"
85
+ image = resize_and_crop(image, (width, height))
86
+ mask = resize_and_crop(mask, (width, height))
87
+ condition_image = resize_and_padding(condition_image, (width, height))
88
+ return image, condition_image, mask
89
+
90
+ def prepare_extra_step_kwargs(self, generator, eta):
91
+ # prepare extra kwargs for the scheduler step, since not all schedulers have the same signature
92
+ # eta (η) is only used with the DDIMScheduler, it will be ignored for other schedulers.
93
+ # eta corresponds to η in DDIM paper: https://arxiv.org/abs/2010.02502
94
+ # and should be between [0, 1]
95
+
96
+ accepts_eta = "eta" in set(
97
+ inspect.signature(self.noise_scheduler.step).parameters.keys()
98
+ )
99
+ extra_step_kwargs = {}
100
+ if accepts_eta:
101
+ extra_step_kwargs["eta"] = eta
102
+
103
+ # check if the scheduler accepts generator
104
+ accepts_generator = "generator" in set(
105
+ inspect.signature(self.noise_scheduler.step).parameters.keys()
106
+ )
107
+ if accepts_generator:
108
+ extra_step_kwargs["generator"] = generator
109
+ return extra_step_kwargs
110
+
111
+ @torch.no_grad()
112
+ def __call__(
113
+ self,
114
+ image: Union[PIL.Image.Image, torch.Tensor],
115
+ condition_image: Union[PIL.Image.Image, torch.Tensor],
116
+ mask: Union[PIL.Image.Image, torch.Tensor],
117
+ num_inference_steps: int = 50,
118
+ guidance_scale: float = 2.5,
119
+ height: int = 1024,
120
+ width: int = 768,
121
+ generator=None,
122
+ eta=1.0,
123
+ **kwargs
124
+ ):
125
+ concat_dim = -2 # FIXME: y axis concat
126
+ # Prepare inputs to Tensor
127
+ image, condition_image, mask = self.check_inputs(image, condition_image, mask, width, height)
128
+ image = prepare_image(image).to(self.device, dtype=self.weight_dtype)
129
+ condition_image = prepare_image(condition_image).to(self.device, dtype=self.weight_dtype)
130
+ mask = prepare_mask_image(mask).to(self.device, dtype=self.weight_dtype)
131
+ # Mask image
132
+ masked_image = image * (mask < 0.5)
133
+ # VAE encoding
134
+ masked_latent = compute_vae_encodings(masked_image, self.vae)
135
+ condition_latent = compute_vae_encodings(condition_image, self.vae)
136
+ mask_latent = torch.nn.functional.interpolate(mask, size=masked_latent.shape[-2:], mode="nearest")
137
+ del image, mask, condition_image
138
+ # Concatenate latents
139
+ masked_latent_concat = torch.cat([masked_latent, condition_latent], dim=concat_dim)
140
+ mask_latent_concat = torch.cat([mask_latent, torch.zeros_like(mask_latent)], dim=concat_dim)
141
+ # Prepare noise
142
+ latents = randn_tensor(
143
+ masked_latent_concat.shape,
144
+ generator=generator,
145
+ device=masked_latent_concat.device,
146
+ dtype=self.weight_dtype,
147
+ )
148
+ # Prepare timesteps
149
+ self.noise_scheduler.set_timesteps(num_inference_steps, device=self.device)
150
+ timesteps = self.noise_scheduler.timesteps
151
+ latents = latents * self.noise_scheduler.init_noise_sigma
152
+ # Classifier-Free Guidance
153
+ if do_classifier_free_guidance := (guidance_scale > 1.0):
154
+ masked_latent_concat = torch.cat(
155
+ [
156
+ torch.cat([masked_latent, torch.zeros_like(condition_latent)], dim=concat_dim),
157
+ masked_latent_concat,
158
+ ]
159
+ )
160
+ mask_latent_concat = torch.cat([mask_latent_concat] * 2)
161
+
162
+ # Denoising loop
163
+ extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
164
+ num_warmup_steps = (len(timesteps) - num_inference_steps * self.noise_scheduler.order)
165
+ with tqdm.tqdm(total=num_inference_steps) as progress_bar:
166
+ for i, t in enumerate(timesteps):
167
+ # expand the latents if we are doing classifier free guidance
168
+ non_inpainting_latent_model_input = (torch.cat([latents] * 2) if do_classifier_free_guidance else latents)
169
+ non_inpainting_latent_model_input = self.noise_scheduler.scale_model_input(non_inpainting_latent_model_input, t)
170
+ # prepare the input for the inpainting model
171
+ inpainting_latent_model_input = torch.cat([non_inpainting_latent_model_input, mask_latent_concat, masked_latent_concat], dim=1)
172
+ # predict the noise residual
173
+ noise_pred= self.unet(
174
+ inpainting_latent_model_input,
175
+ t.to(self.device),
176
+ encoder_hidden_states=None, # FIXME
177
+ return_dict=False,
178
+ )[0]
179
+ # perform guidance
180
+ if do_classifier_free_guidance:
181
+ noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
182
+ noise_pred = noise_pred_uncond + guidance_scale * (
183
+ noise_pred_text - noise_pred_uncond
184
+ )
185
+ # compute the previous noisy sample x_t -> x_t-1
186
+ latents = self.noise_scheduler.step(
187
+ noise_pred, t, latents, **extra_step_kwargs
188
+ ).prev_sample
189
+ # call the callback, if provided
190
+ if i == len(timesteps) - 1 or (
191
+ (i + 1) > num_warmup_steps
192
+ and (i + 1) % self.noise_scheduler.order == 0
193
+ ):
194
+ progress_bar.update()
195
+
196
+ # Decode the final latents
197
+ latents = latents.split(latents.shape[concat_dim] // 2, dim=concat_dim)[0]
198
+ latents = 1 / self.vae.config.scaling_factor * latents
199
+ image = self.vae.decode(latents.to(self.device, dtype=self.weight_dtype)).sample
200
+ image = (image / 2 + 0.5).clamp(0, 1)
201
+ # we always cast to float32 as this does not cause significant overhead and is compatible with bfloat16
202
+ image = image.cpu().permute(0, 2, 3, 1).float().numpy()
203
+ image = numpy_to_pil(image)
204
+
205
+ # Safety Check
206
+ if not self.skip_safety_check:
207
+ current_script_directory = os.path.dirname(os.path.realpath(__file__))
208
+ nsfw_image_path = os.path.join(
209
+ os.path.dirname(current_script_directory), "resource", "img", "NSFW.jpg"
210
+ )
211
+
212
+ image_np = np.array(image)
213
+ _, has_nsfw_concept = self.run_safety_checker(image=image_np)
214
+
215
+ # Deployed HF Spaces may not include the placeholder NSFW image.
216
+ # If missing, skip replacement but still return the generated result.
217
+ nsfw_image = None
218
+ if os.path.exists(nsfw_image_path):
219
+ nsfw_image = PIL.Image.open(nsfw_image_path).resize(image[0].size)
220
+
221
+ for i, not_safe in enumerate(has_nsfw_concept):
222
+ if not_safe and nsfw_image is not None:
223
+ image[i] = nsfw_image
224
+ return image
225
+
226
+
227
+ class CatVTONPix2PixPipeline(CatVTONPipeline):
228
+ def auto_attn_ckpt_load(self, attn_ckpt, version):
229
+ # TODO: Temperal fix for the model version
230
+ if os.path.exists(attn_ckpt):
231
+ load_checkpoint_in_model(self.attn_modules, os.path.join(attn_ckpt, version, 'attention'))
232
+ else:
233
+ repo_path = snapshot_download(repo_id=attn_ckpt)
234
+ print(f"Downloaded {attn_ckpt} to {repo_path}")
235
+ load_checkpoint_in_model(self.attn_modules, os.path.join(repo_path, version, 'attention'))
236
+
237
+ def check_inputs(self, image, condition_image, width, height):
238
+ if isinstance(image, torch.Tensor) and isinstance(condition_image, torch.Tensor) and isinstance(torch.Tensor):
239
+ return image, condition_image
240
+ image = resize_and_crop(image, (width, height))
241
+ condition_image = resize_and_padding(condition_image, (width, height))
242
+ return image, condition_image
243
+
244
+ @torch.no_grad()
245
+ def __call__(
246
+ self,
247
+ image: Union[PIL.Image.Image, torch.Tensor],
248
+ condition_image: Union[PIL.Image.Image, torch.Tensor],
249
+ num_inference_steps: int = 50,
250
+ guidance_scale: float = 2.5,
251
+ height: int = 1024,
252
+ width: int = 768,
253
+ generator=None,
254
+ eta=1.0,
255
+ **kwargs
256
+ ):
257
+ concat_dim = -1
258
+ # Prepare inputs to Tensor
259
+ image, condition_image = self.check_inputs(image, condition_image, width, height)
260
+ image = prepare_image(image).to(self.device, dtype=self.weight_dtype)
261
+ condition_image = prepare_image(condition_image).to(self.device, dtype=self.weight_dtype)
262
+ # VAE encoding
263
+ image_latent = compute_vae_encodings(image, self.vae)
264
+ condition_latent = compute_vae_encodings(condition_image, self.vae)
265
+ del image, condition_image
266
+ # Concatenate latents
267
+ condition_latent_concat = torch.cat([image_latent, condition_latent], dim=concat_dim)
268
+ # Prepare noise
269
+ latents = randn_tensor(
270
+ condition_latent_concat.shape,
271
+ generator=generator,
272
+ device=condition_latent_concat.device,
273
+ dtype=self.weight_dtype,
274
+ )
275
+ # Prepare timesteps
276
+ self.noise_scheduler.set_timesteps(num_inference_steps, device=self.device)
277
+ timesteps = self.noise_scheduler.timesteps
278
+ latents = latents * self.noise_scheduler.init_noise_sigma
279
+ # Classifier-Free Guidance
280
+ if do_classifier_free_guidance := (guidance_scale > 1.0):
281
+ condition_latent_concat = torch.cat(
282
+ [
283
+ torch.cat([image_latent, torch.zeros_like(condition_latent)], dim=concat_dim),
284
+ condition_latent_concat,
285
+ ]
286
+ )
287
+
288
+ # Denoising loop
289
+ extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
290
+ num_warmup_steps = (len(timesteps) - num_inference_steps * self.noise_scheduler.order)
291
+ with tqdm.tqdm(total=num_inference_steps) as progress_bar:
292
+ for i, t in enumerate(timesteps):
293
+ # expand the latents if we are doing classifier free guidance
294
+ latent_model_input = (torch.cat([latents] * 2) if do_classifier_free_guidance else latents)
295
+ latent_model_input = self.noise_scheduler.scale_model_input(latent_model_input, t)
296
+ # prepare the input for the inpainting model
297
+ p2p_latent_model_input = torch.cat([latent_model_input, condition_latent_concat], dim=1)
298
+ # predict the noise residual
299
+ noise_pred= self.unet(
300
+ p2p_latent_model_input,
301
+ t.to(self.device),
302
+ encoder_hidden_states=None,
303
+ return_dict=False,
304
+ )[0]
305
+ # perform guidance
306
+ if do_classifier_free_guidance:
307
+ noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
308
+ noise_pred = noise_pred_uncond + guidance_scale * (
309
+ noise_pred_text - noise_pred_uncond
310
+ )
311
+ # compute the previous noisy sample x_t -> x_t-1
312
+ latents = self.noise_scheduler.step(
313
+ noise_pred, t, latents, **extra_step_kwargs
314
+ ).prev_sample
315
+ # call the callback, if provided
316
+ if i == len(timesteps) - 1 or (
317
+ (i + 1) > num_warmup_steps
318
+ and (i + 1) % self.noise_scheduler.order == 0
319
+ ):
320
+ progress_bar.update()
321
+
322
+ # Decode the final latents
323
+ latents = latents.split(latents.shape[concat_dim] // 2, dim=concat_dim)[0]
324
+ latents = 1 / self.vae.config.scaling_factor * latents
325
+ image = self.vae.decode(latents.to(self.device, dtype=self.weight_dtype)).sample
326
+ image = (image / 2 + 0.5).clamp(0, 1)
327
+ # we always cast to float32 as this does not cause significant overhead and is compatible with bfloat16
328
+ image = image.cpu().permute(0, 2, 3, 1).float().numpy()
329
+ image = numpy_to_pil(image)
330
+
331
+ # Safety Check
332
+ if not self.skip_safety_check:
333
+ current_script_directory = os.path.dirname(os.path.realpath(__file__))
334
+ nsfw_image = os.path.join(os.path.dirname(current_script_directory), 'resource', 'img', 'NSFW.jpg')
335
+ nsfw_image = PIL.Image.open(nsfw_image).resize(image[0].size)
336
+ image_np = np.array(image)
337
+ _, has_nsfw_concept = self.run_safety_checker(image=image_np)
338
+ for i, not_safe in enumerate(has_nsfw_concept):
339
+ if not_safe:
340
+ image[i] = nsfw_image
341
+ return image
.history/CatVTON/model/pipeline_20260618144455.py ADDED
@@ -0,0 +1,341 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import inspect
2
+ import os
3
+ from typing import Union
4
+
5
+ import PIL
6
+ import numpy as np
7
+ import torch
8
+ import tqdm
9
+ from accelerate import load_checkpoint_in_model
10
+ from diffusers import AutoencoderKL, DDIMScheduler, UNet2DConditionModel
11
+ from diffusers.pipelines.stable_diffusion.safety_checker import \
12
+ StableDiffusionSafetyChecker
13
+ from diffusers.utils.torch_utils import randn_tensor
14
+ from huggingface_hub import snapshot_download
15
+ from transformers import CLIPImageProcessor
16
+
17
+ from model.attn_processor import SkipAttnProcessor
18
+ from model.utils import get_trainable_module, init_adapter
19
+ from utils import (compute_vae_encodings, numpy_to_pil, prepare_image,
20
+ prepare_mask_image, resize_and_crop, resize_and_padding)
21
+
22
+
23
+ class CatVTONPipeline:
24
+ def __init__(
25
+ self,
26
+ base_ckpt,
27
+ attn_ckpt,
28
+ attn_ckpt_version="mix",
29
+ weight_dtype=torch.float32,
30
+ device='cuda',
31
+ compile=False,
32
+ skip_safety_check=False,
33
+ use_tf32=True,
34
+ ):
35
+ self.device = device
36
+ self.weight_dtype = weight_dtype
37
+ self.skip_safety_check = skip_safety_check
38
+
39
+ self.noise_scheduler = DDIMScheduler.from_pretrained(base_ckpt, subfolder="scheduler")
40
+ self.vae = AutoencoderKL.from_pretrained("stabilityai/sd-vae-ft-mse").to(device, dtype=weight_dtype)
41
+ if not skip_safety_check:
42
+ self.feature_extractor = CLIPImageProcessor.from_pretrained(base_ckpt, subfolder="feature_extractor")
43
+ self.safety_checker = StableDiffusionSafetyChecker.from_pretrained(base_ckpt, subfolder="safety_checker").to(device, dtype=weight_dtype)
44
+ self.unet = UNet2DConditionModel.from_pretrained(base_ckpt, subfolder="unet").to(device, dtype=weight_dtype)
45
+ init_adapter(self.unet, cross_attn_cls=SkipAttnProcessor) # Skip Cross-Attention
46
+ self.attn_modules = get_trainable_module(self.unet, "attention")
47
+ self.auto_attn_ckpt_load(attn_ckpt, attn_ckpt_version)
48
+ # Pytorch 2.0 Compile
49
+ if compile:
50
+ self.unet = torch.compile(self.unet)
51
+ self.vae = torch.compile(self.vae, mode="reduce-overhead")
52
+
53
+ # Enable TF32 for faster training on Ampere GPUs (A100 and RTX 30 series).
54
+ if use_tf32:
55
+ torch.set_float32_matmul_precision("high")
56
+ torch.backends.cuda.matmul.allow_tf32 = True
57
+
58
+ def auto_attn_ckpt_load(self, attn_ckpt, version):
59
+ sub_folder = {
60
+ "mix": "mix-48k-1024",
61
+ "vitonhd": "vitonhd-16k-512",
62
+ "dresscode": "dresscode-16k-512",
63
+ }[version]
64
+ if os.path.exists(attn_ckpt):
65
+ load_checkpoint_in_model(self.attn_modules, os.path.join(attn_ckpt, sub_folder, 'attention'))
66
+ else:
67
+ repo_path = snapshot_download(repo_id=attn_ckpt)
68
+ print(f"Downloaded {attn_ckpt} to {repo_path}")
69
+ load_checkpoint_in_model(self.attn_modules, os.path.join(repo_path, sub_folder, 'attention'))
70
+
71
+ def run_safety_checker(self, image):
72
+ if self.safety_checker is None:
73
+ has_nsfw_concept = None
74
+ else:
75
+ safety_checker_input = self.feature_extractor(image, return_tensors="pt").to(self.device)
76
+ image, has_nsfw_concept = self.safety_checker(
77
+ images=image, clip_input=safety_checker_input.pixel_values.to(self.weight_dtype)
78
+ )
79
+ return image, has_nsfw_concept
80
+
81
+ def check_inputs(self, image, condition_image, mask, width, height):
82
+ if isinstance(image, torch.Tensor) and isinstance(condition_image, torch.Tensor) and isinstance(mask, torch.Tensor):
83
+ return image, condition_image, mask
84
+ assert image.size == mask.size, "Image and mask must have the same size"
85
+ image = resize_and_crop(image, (width, height))
86
+ mask = resize_and_crop(mask, (width, height))
87
+ condition_image = resize_and_padding(condition_image, (width, height))
88
+ return image, condition_image, mask
89
+
90
+ def prepare_extra_step_kwargs(self, generator, eta):
91
+ # prepare extra kwargs for the scheduler step, since not all schedulers have the same signature
92
+ # eta (η) is only used with the DDIMScheduler, it will be ignored for other schedulers.
93
+ # eta corresponds to η in DDIM paper: https://arxiv.org/abs/2010.02502
94
+ # and should be between [0, 1]
95
+
96
+ accepts_eta = "eta" in set(
97
+ inspect.signature(self.noise_scheduler.step).parameters.keys()
98
+ )
99
+ extra_step_kwargs = {}
100
+ if accepts_eta:
101
+ extra_step_kwargs["eta"] = eta
102
+
103
+ # check if the scheduler accepts generator
104
+ accepts_generator = "generator" in set(
105
+ inspect.signature(self.noise_scheduler.step).parameters.keys()
106
+ )
107
+ if accepts_generator:
108
+ extra_step_kwargs["generator"] = generator
109
+ return extra_step_kwargs
110
+
111
+ @torch.no_grad()
112
+ def __call__(
113
+ self,
114
+ image: Union[PIL.Image.Image, torch.Tensor],
115
+ condition_image: Union[PIL.Image.Image, torch.Tensor],
116
+ mask: Union[PIL.Image.Image, torch.Tensor],
117
+ num_inference_steps: int = 50,
118
+ guidance_scale: float = 2.5,
119
+ height: int = 1024,
120
+ width: int = 768,
121
+ generator=None,
122
+ eta=1.0,
123
+ **kwargs
124
+ ):
125
+ concat_dim = -2 # FIXME: y axis concat
126
+ # Prepare inputs to Tensor
127
+ image, condition_image, mask = self.check_inputs(image, condition_image, mask, width, height)
128
+ image = prepare_image(image).to(self.device, dtype=self.weight_dtype)
129
+ condition_image = prepare_image(condition_image).to(self.device, dtype=self.weight_dtype)
130
+ mask = prepare_mask_image(mask).to(self.device, dtype=self.weight_dtype)
131
+ # Mask image
132
+ masked_image = image * (mask < 0.5)
133
+ # VAE encoding
134
+ masked_latent = compute_vae_encodings(masked_image, self.vae)
135
+ condition_latent = compute_vae_encodings(condition_image, self.vae)
136
+ mask_latent = torch.nn.functional.interpolate(mask, size=masked_latent.shape[-2:], mode="nearest")
137
+ del image, mask, condition_image
138
+ # Concatenate latents
139
+ masked_latent_concat = torch.cat([masked_latent, condition_latent], dim=concat_dim)
140
+ mask_latent_concat = torch.cat([mask_latent, torch.zeros_like(mask_latent)], dim=concat_dim)
141
+ # Prepare noise
142
+ latents = randn_tensor(
143
+ masked_latent_concat.shape,
144
+ generator=generator,
145
+ device=masked_latent_concat.device,
146
+ dtype=self.weight_dtype,
147
+ )
148
+ # Prepare timesteps
149
+ self.noise_scheduler.set_timesteps(num_inference_steps, device=self.device)
150
+ timesteps = self.noise_scheduler.timesteps
151
+ latents = latents * self.noise_scheduler.init_noise_sigma
152
+ # Classifier-Free Guidance
153
+ if do_classifier_free_guidance := (guidance_scale > 1.0):
154
+ masked_latent_concat = torch.cat(
155
+ [
156
+ torch.cat([masked_latent, torch.zeros_like(condition_latent)], dim=concat_dim),
157
+ masked_latent_concat,
158
+ ]
159
+ )
160
+ mask_latent_concat = torch.cat([mask_latent_concat] * 2)
161
+
162
+ # Denoising loop
163
+ extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
164
+ num_warmup_steps = (len(timesteps) - num_inference_steps * self.noise_scheduler.order)
165
+ with tqdm.tqdm(total=num_inference_steps) as progress_bar:
166
+ for i, t in enumerate(timesteps):
167
+ # expand the latents if we are doing classifier free guidance
168
+ non_inpainting_latent_model_input = (torch.cat([latents] * 2) if do_classifier_free_guidance else latents)
169
+ non_inpainting_latent_model_input = self.noise_scheduler.scale_model_input(non_inpainting_latent_model_input, t)
170
+ # prepare the input for the inpainting model
171
+ inpainting_latent_model_input = torch.cat([non_inpainting_latent_model_input, mask_latent_concat, masked_latent_concat], dim=1)
172
+ # predict the noise residual
173
+ noise_pred= self.unet(
174
+ inpainting_latent_model_input,
175
+ t.to(self.device),
176
+ encoder_hidden_states=None, # FIXME
177
+ return_dict=False,
178
+ )[0]
179
+ # perform guidance
180
+ if do_classifier_free_guidance:
181
+ noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
182
+ noise_pred = noise_pred_uncond + guidance_scale * (
183
+ noise_pred_text - noise_pred_uncond
184
+ )
185
+ # compute the previous noisy sample x_t -> x_t-1
186
+ latents = self.noise_scheduler.step(
187
+ noise_pred, t, latents, **extra_step_kwargs
188
+ ).prev_sample
189
+ # call the callback, if provided
190
+ if i == len(timesteps) - 1 or (
191
+ (i + 1) > num_warmup_steps
192
+ and (i + 1) % self.noise_scheduler.order == 0
193
+ ):
194
+ progress_bar.update()
195
+
196
+ # Decode the final latents
197
+ latents = latents.split(latents.shape[concat_dim] // 2, dim=concat_dim)[0]
198
+ latents = 1 / self.vae.config.scaling_factor * latents
199
+ image = self.vae.decode(latents.to(self.device, dtype=self.weight_dtype)).sample
200
+ image = (image / 2 + 0.5).clamp(0, 1)
201
+ # we always cast to float32 as this does not cause significant overhead and is compatible with bfloat16
202
+ image = image.cpu().permute(0, 2, 3, 1).float().numpy()
203
+ image = numpy_to_pil(image)
204
+
205
+ # Safety Check
206
+ if not self.skip_safety_check:
207
+ current_script_directory = os.path.dirname(os.path.realpath(__file__))
208
+ nsfw_image_path = os.path.join(
209
+ os.path.dirname(current_script_directory), "resource", "img", "NSFW.jpg"
210
+ )
211
+
212
+ image_np = np.array(image)
213
+ _, has_nsfw_concept = self.run_safety_checker(image=image_np)
214
+
215
+ # Deployed HF Spaces may not include the placeholder NSFW image.
216
+ # If missing, skip replacement but still return the generated result.
217
+ nsfw_image = None
218
+ if os.path.exists(nsfw_image_path):
219
+ nsfw_image = PIL.Image.open(nsfw_image_path).resize(image[0].size)
220
+
221
+ for i, not_safe in enumerate(has_nsfw_concept):
222
+ if not_safe and nsfw_image is not None:
223
+ image[i] = nsfw_image
224
+ return image
225
+
226
+
227
+ class CatVTONPix2PixPipeline(CatVTONPipeline):
228
+ def auto_attn_ckpt_load(self, attn_ckpt, version):
229
+ # TODO: Temperal fix for the model version
230
+ if os.path.exists(attn_ckpt):
231
+ load_checkpoint_in_model(self.attn_modules, os.path.join(attn_ckpt, version, 'attention'))
232
+ else:
233
+ repo_path = snapshot_download(repo_id=attn_ckpt)
234
+ print(f"Downloaded {attn_ckpt} to {repo_path}")
235
+ load_checkpoint_in_model(self.attn_modules, os.path.join(repo_path, version, 'attention'))
236
+
237
+ def check_inputs(self, image, condition_image, width, height):
238
+ if isinstance(image, torch.Tensor) and isinstance(condition_image, torch.Tensor) and isinstance(torch.Tensor):
239
+ return image, condition_image
240
+ image = resize_and_crop(image, (width, height))
241
+ condition_image = resize_and_padding(condition_image, (width, height))
242
+ return image, condition_image
243
+
244
+ @torch.no_grad()
245
+ def __call__(
246
+ self,
247
+ image: Union[PIL.Image.Image, torch.Tensor],
248
+ condition_image: Union[PIL.Image.Image, torch.Tensor],
249
+ num_inference_steps: int = 50,
250
+ guidance_scale: float = 2.5,
251
+ height: int = 1024,
252
+ width: int = 768,
253
+ generator=None,
254
+ eta=1.0,
255
+ **kwargs
256
+ ):
257
+ concat_dim = -1
258
+ # Prepare inputs to Tensor
259
+ image, condition_image = self.check_inputs(image, condition_image, width, height)
260
+ image = prepare_image(image).to(self.device, dtype=self.weight_dtype)
261
+ condition_image = prepare_image(condition_image).to(self.device, dtype=self.weight_dtype)
262
+ # VAE encoding
263
+ image_latent = compute_vae_encodings(image, self.vae)
264
+ condition_latent = compute_vae_encodings(condition_image, self.vae)
265
+ del image, condition_image
266
+ # Concatenate latents
267
+ condition_latent_concat = torch.cat([image_latent, condition_latent], dim=concat_dim)
268
+ # Prepare noise
269
+ latents = randn_tensor(
270
+ condition_latent_concat.shape,
271
+ generator=generator,
272
+ device=condition_latent_concat.device,
273
+ dtype=self.weight_dtype,
274
+ )
275
+ # Prepare timesteps
276
+ self.noise_scheduler.set_timesteps(num_inference_steps, device=self.device)
277
+ timesteps = self.noise_scheduler.timesteps
278
+ latents = latents * self.noise_scheduler.init_noise_sigma
279
+ # Classifier-Free Guidance
280
+ if do_classifier_free_guidance := (guidance_scale > 1.0):
281
+ condition_latent_concat = torch.cat(
282
+ [
283
+ torch.cat([image_latent, torch.zeros_like(condition_latent)], dim=concat_dim),
284
+ condition_latent_concat,
285
+ ]
286
+ )
287
+
288
+ # Denoising loop
289
+ extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
290
+ num_warmup_steps = (len(timesteps) - num_inference_steps * self.noise_scheduler.order)
291
+ with tqdm.tqdm(total=num_inference_steps) as progress_bar:
292
+ for i, t in enumerate(timesteps):
293
+ # expand the latents if we are doing classifier free guidance
294
+ latent_model_input = (torch.cat([latents] * 2) if do_classifier_free_guidance else latents)
295
+ latent_model_input = self.noise_scheduler.scale_model_input(latent_model_input, t)
296
+ # prepare the input for the inpainting model
297
+ p2p_latent_model_input = torch.cat([latent_model_input, condition_latent_concat], dim=1)
298
+ # predict the noise residual
299
+ noise_pred= self.unet(
300
+ p2p_latent_model_input,
301
+ t.to(self.device),
302
+ encoder_hidden_states=None,
303
+ return_dict=False,
304
+ )[0]
305
+ # perform guidance
306
+ if do_classifier_free_guidance:
307
+ noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
308
+ noise_pred = noise_pred_uncond + guidance_scale * (
309
+ noise_pred_text - noise_pred_uncond
310
+ )
311
+ # compute the previous noisy sample x_t -> x_t-1
312
+ latents = self.noise_scheduler.step(
313
+ noise_pred, t, latents, **extra_step_kwargs
314
+ ).prev_sample
315
+ # call the callback, if provided
316
+ if i == len(timesteps) - 1 or (
317
+ (i + 1) > num_warmup_steps
318
+ and (i + 1) % self.noise_scheduler.order == 0
319
+ ):
320
+ progress_bar.update()
321
+
322
+ # Decode the final latents
323
+ latents = latents.split(latents.shape[concat_dim] // 2, dim=concat_dim)[0]
324
+ latents = 1 / self.vae.config.scaling_factor * latents
325
+ image = self.vae.decode(latents.to(self.device, dtype=self.weight_dtype)).sample
326
+ image = (image / 2 + 0.5).clamp(0, 1)
327
+ # we always cast to float32 as this does not cause significant overhead and is compatible with bfloat16
328
+ image = image.cpu().permute(0, 2, 3, 1).float().numpy()
329
+ image = numpy_to_pil(image)
330
+
331
+ # Safety Check
332
+ if not self.skip_safety_check:
333
+ current_script_directory = os.path.dirname(os.path.realpath(__file__))
334
+ nsfw_image = os.path.join(os.path.dirname(current_script_directory), 'resource', 'img', 'NSFW.jpg')
335
+ nsfw_image = PIL.Image.open(nsfw_image).resize(image[0].size)
336
+ image_np = np.array(image)
337
+ _, has_nsfw_concept = self.run_safety_checker(image=image_np)
338
+ for i, not_safe in enumerate(has_nsfw_concept):
339
+ if not_safe:
340
+ image[i] = nsfw_image
341
+ return image
.history/CatVTON/model/pipeline_20260618144517.py ADDED
@@ -0,0 +1,348 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import inspect
2
+ import os
3
+ from typing import Union
4
+
5
+ import PIL
6
+ import numpy as np
7
+ import torch
8
+ import tqdm
9
+ from accelerate import load_checkpoint_in_model
10
+ from diffusers import AutoencoderKL, DDIMScheduler, UNet2DConditionModel
11
+ from diffusers.pipelines.stable_diffusion.safety_checker import \
12
+ StableDiffusionSafetyChecker
13
+ from diffusers.utils.torch_utils import randn_tensor
14
+ from huggingface_hub import snapshot_download
15
+ from transformers import CLIPImageProcessor
16
+
17
+ from model.attn_processor import SkipAttnProcessor
18
+ from model.utils import get_trainable_module, init_adapter
19
+ from utils import (compute_vae_encodings, numpy_to_pil, prepare_image,
20
+ prepare_mask_image, resize_and_crop, resize_and_padding)
21
+
22
+
23
+ class CatVTONPipeline:
24
+ def __init__(
25
+ self,
26
+ base_ckpt,
27
+ attn_ckpt,
28
+ attn_ckpt_version="mix",
29
+ weight_dtype=torch.float32,
30
+ device='cuda',
31
+ compile=False,
32
+ skip_safety_check=False,
33
+ use_tf32=True,
34
+ ):
35
+ self.device = device
36
+ self.weight_dtype = weight_dtype
37
+ self.skip_safety_check = skip_safety_check
38
+
39
+ self.noise_scheduler = DDIMScheduler.from_pretrained(base_ckpt, subfolder="scheduler")
40
+ self.vae = AutoencoderKL.from_pretrained("stabilityai/sd-vae-ft-mse").to(device, dtype=weight_dtype)
41
+ if not skip_safety_check:
42
+ self.feature_extractor = CLIPImageProcessor.from_pretrained(base_ckpt, subfolder="feature_extractor")
43
+ self.safety_checker = StableDiffusionSafetyChecker.from_pretrained(base_ckpt, subfolder="safety_checker").to(device, dtype=weight_dtype)
44
+ self.unet = UNet2DConditionModel.from_pretrained(base_ckpt, subfolder="unet").to(device, dtype=weight_dtype)
45
+ init_adapter(self.unet, cross_attn_cls=SkipAttnProcessor) # Skip Cross-Attention
46
+ self.attn_modules = get_trainable_module(self.unet, "attention")
47
+ self.auto_attn_ckpt_load(attn_ckpt, attn_ckpt_version)
48
+ # Pytorch 2.0 Compile
49
+ if compile:
50
+ self.unet = torch.compile(self.unet)
51
+ self.vae = torch.compile(self.vae, mode="reduce-overhead")
52
+
53
+ # Enable TF32 for faster training on Ampere GPUs (A100 and RTX 30 series).
54
+ if use_tf32:
55
+ torch.set_float32_matmul_precision("high")
56
+ torch.backends.cuda.matmul.allow_tf32 = True
57
+
58
+ def auto_attn_ckpt_load(self, attn_ckpt, version):
59
+ sub_folder = {
60
+ "mix": "mix-48k-1024",
61
+ "vitonhd": "vitonhd-16k-512",
62
+ "dresscode": "dresscode-16k-512",
63
+ }[version]
64
+ if os.path.exists(attn_ckpt):
65
+ load_checkpoint_in_model(self.attn_modules, os.path.join(attn_ckpt, sub_folder, 'attention'))
66
+ else:
67
+ repo_path = snapshot_download(repo_id=attn_ckpt)
68
+ print(f"Downloaded {attn_ckpt} to {repo_path}")
69
+ load_checkpoint_in_model(self.attn_modules, os.path.join(repo_path, sub_folder, 'attention'))
70
+
71
+ def run_safety_checker(self, image):
72
+ if self.safety_checker is None:
73
+ has_nsfw_concept = None
74
+ else:
75
+ safety_checker_input = self.feature_extractor(image, return_tensors="pt").to(self.device)
76
+ image, has_nsfw_concept = self.safety_checker(
77
+ images=image, clip_input=safety_checker_input.pixel_values.to(self.weight_dtype)
78
+ )
79
+ return image, has_nsfw_concept
80
+
81
+ def check_inputs(self, image, condition_image, mask, width, height):
82
+ if isinstance(image, torch.Tensor) and isinstance(condition_image, torch.Tensor) and isinstance(mask, torch.Tensor):
83
+ return image, condition_image, mask
84
+ assert image.size == mask.size, "Image and mask must have the same size"
85
+ image = resize_and_crop(image, (width, height))
86
+ mask = resize_and_crop(mask, (width, height))
87
+ condition_image = resize_and_padding(condition_image, (width, height))
88
+ return image, condition_image, mask
89
+
90
+ def prepare_extra_step_kwargs(self, generator, eta):
91
+ # prepare extra kwargs for the scheduler step, since not all schedulers have the same signature
92
+ # eta (η) is only used with the DDIMScheduler, it will be ignored for other schedulers.
93
+ # eta corresponds to η in DDIM paper: https://arxiv.org/abs/2010.02502
94
+ # and should be between [0, 1]
95
+
96
+ accepts_eta = "eta" in set(
97
+ inspect.signature(self.noise_scheduler.step).parameters.keys()
98
+ )
99
+ extra_step_kwargs = {}
100
+ if accepts_eta:
101
+ extra_step_kwargs["eta"] = eta
102
+
103
+ # check if the scheduler accepts generator
104
+ accepts_generator = "generator" in set(
105
+ inspect.signature(self.noise_scheduler.step).parameters.keys()
106
+ )
107
+ if accepts_generator:
108
+ extra_step_kwargs["generator"] = generator
109
+ return extra_step_kwargs
110
+
111
+ @torch.no_grad()
112
+ def __call__(
113
+ self,
114
+ image: Union[PIL.Image.Image, torch.Tensor],
115
+ condition_image: Union[PIL.Image.Image, torch.Tensor],
116
+ mask: Union[PIL.Image.Image, torch.Tensor],
117
+ num_inference_steps: int = 50,
118
+ guidance_scale: float = 2.5,
119
+ height: int = 1024,
120
+ width: int = 768,
121
+ generator=None,
122
+ eta=1.0,
123
+ **kwargs
124
+ ):
125
+ concat_dim = -2 # FIXME: y axis concat
126
+ # Prepare inputs to Tensor
127
+ image, condition_image, mask = self.check_inputs(image, condition_image, mask, width, height)
128
+ image = prepare_image(image).to(self.device, dtype=self.weight_dtype)
129
+ condition_image = prepare_image(condition_image).to(self.device, dtype=self.weight_dtype)
130
+ mask = prepare_mask_image(mask).to(self.device, dtype=self.weight_dtype)
131
+ # Mask image
132
+ masked_image = image * (mask < 0.5)
133
+ # VAE encoding
134
+ masked_latent = compute_vae_encodings(masked_image, self.vae)
135
+ condition_latent = compute_vae_encodings(condition_image, self.vae)
136
+ mask_latent = torch.nn.functional.interpolate(mask, size=masked_latent.shape[-2:], mode="nearest")
137
+ del image, mask, condition_image
138
+ # Concatenate latents
139
+ masked_latent_concat = torch.cat([masked_latent, condition_latent], dim=concat_dim)
140
+ mask_latent_concat = torch.cat([mask_latent, torch.zeros_like(mask_latent)], dim=concat_dim)
141
+ # Prepare noise
142
+ latents = randn_tensor(
143
+ masked_latent_concat.shape,
144
+ generator=generator,
145
+ device=masked_latent_concat.device,
146
+ dtype=self.weight_dtype,
147
+ )
148
+ # Prepare timesteps
149
+ self.noise_scheduler.set_timesteps(num_inference_steps, device=self.device)
150
+ timesteps = self.noise_scheduler.timesteps
151
+ latents = latents * self.noise_scheduler.init_noise_sigma
152
+ # Classifier-Free Guidance
153
+ if do_classifier_free_guidance := (guidance_scale > 1.0):
154
+ masked_latent_concat = torch.cat(
155
+ [
156
+ torch.cat([masked_latent, torch.zeros_like(condition_latent)], dim=concat_dim),
157
+ masked_latent_concat,
158
+ ]
159
+ )
160
+ mask_latent_concat = torch.cat([mask_latent_concat] * 2)
161
+
162
+ # Denoising loop
163
+ extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
164
+ num_warmup_steps = (len(timesteps) - num_inference_steps * self.noise_scheduler.order)
165
+ with tqdm.tqdm(total=num_inference_steps) as progress_bar:
166
+ for i, t in enumerate(timesteps):
167
+ # expand the latents if we are doing classifier free guidance
168
+ non_inpainting_latent_model_input = (torch.cat([latents] * 2) if do_classifier_free_guidance else latents)
169
+ non_inpainting_latent_model_input = self.noise_scheduler.scale_model_input(non_inpainting_latent_model_input, t)
170
+ # prepare the input for the inpainting model
171
+ inpainting_latent_model_input = torch.cat([non_inpainting_latent_model_input, mask_latent_concat, masked_latent_concat], dim=1)
172
+ # predict the noise residual
173
+ noise_pred= self.unet(
174
+ inpainting_latent_model_input,
175
+ t.to(self.device),
176
+ encoder_hidden_states=None, # FIXME
177
+ return_dict=False,
178
+ )[0]
179
+ # perform guidance
180
+ if do_classifier_free_guidance:
181
+ noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
182
+ noise_pred = noise_pred_uncond + guidance_scale * (
183
+ noise_pred_text - noise_pred_uncond
184
+ )
185
+ # compute the previous noisy sample x_t -> x_t-1
186
+ latents = self.noise_scheduler.step(
187
+ noise_pred, t, latents, **extra_step_kwargs
188
+ ).prev_sample
189
+ # call the callback, if provided
190
+ if i == len(timesteps) - 1 or (
191
+ (i + 1) > num_warmup_steps
192
+ and (i + 1) % self.noise_scheduler.order == 0
193
+ ):
194
+ progress_bar.update()
195
+
196
+ # Decode the final latents
197
+ latents = latents.split(latents.shape[concat_dim] // 2, dim=concat_dim)[0]
198
+ latents = 1 / self.vae.config.scaling_factor * latents
199
+ image = self.vae.decode(latents.to(self.device, dtype=self.weight_dtype)).sample
200
+ image = (image / 2 + 0.5).clamp(0, 1)
201
+ # we always cast to float32 as this does not cause significant overhead and is compatible with bfloat16
202
+ image = image.cpu().permute(0, 2, 3, 1).float().numpy()
203
+ image = numpy_to_pil(image)
204
+
205
+ # Safety Check
206
+ if not self.skip_safety_check:
207
+ current_script_directory = os.path.dirname(os.path.realpath(__file__))
208
+ nsfw_image_path = os.path.join(
209
+ os.path.dirname(current_script_directory), "resource", "img", "NSFW.jpg"
210
+ )
211
+
212
+ image_np = np.array(image)
213
+ _, has_nsfw_concept = self.run_safety_checker(image=image_np)
214
+
215
+ # Deployed HF Spaces may not include the placeholder NSFW image.
216
+ # If missing, skip replacement but still return the generated result.
217
+ nsfw_image = None
218
+ if os.path.exists(nsfw_image_path):
219
+ nsfw_image = PIL.Image.open(nsfw_image_path).resize(image[0].size)
220
+
221
+ for i, not_safe in enumerate(has_nsfw_concept):
222
+ if not_safe and nsfw_image is not None:
223
+ image[i] = nsfw_image
224
+ return image
225
+
226
+
227
+ class CatVTONPix2PixPipeline(CatVTONPipeline):
228
+ def auto_attn_ckpt_load(self, attn_ckpt, version):
229
+ # TODO: Temperal fix for the model version
230
+ if os.path.exists(attn_ckpt):
231
+ load_checkpoint_in_model(self.attn_modules, os.path.join(attn_ckpt, version, 'attention'))
232
+ else:
233
+ repo_path = snapshot_download(repo_id=attn_ckpt)
234
+ print(f"Downloaded {attn_ckpt} to {repo_path}")
235
+ load_checkpoint_in_model(self.attn_modules, os.path.join(repo_path, version, 'attention'))
236
+
237
+ def check_inputs(self, image, condition_image, width, height):
238
+ if isinstance(image, torch.Tensor) and isinstance(condition_image, torch.Tensor) and isinstance(torch.Tensor):
239
+ return image, condition_image
240
+ image = resize_and_crop(image, (width, height))
241
+ condition_image = resize_and_padding(condition_image, (width, height))
242
+ return image, condition_image
243
+
244
+ @torch.no_grad()
245
+ def __call__(
246
+ self,
247
+ image: Union[PIL.Image.Image, torch.Tensor],
248
+ condition_image: Union[PIL.Image.Image, torch.Tensor],
249
+ num_inference_steps: int = 50,
250
+ guidance_scale: float = 2.5,
251
+ height: int = 1024,
252
+ width: int = 768,
253
+ generator=None,
254
+ eta=1.0,
255
+ **kwargs
256
+ ):
257
+ concat_dim = -1
258
+ # Prepare inputs to Tensor
259
+ image, condition_image = self.check_inputs(image, condition_image, width, height)
260
+ image = prepare_image(image).to(self.device, dtype=self.weight_dtype)
261
+ condition_image = prepare_image(condition_image).to(self.device, dtype=self.weight_dtype)
262
+ # VAE encoding
263
+ image_latent = compute_vae_encodings(image, self.vae)
264
+ condition_latent = compute_vae_encodings(condition_image, self.vae)
265
+ del image, condition_image
266
+ # Concatenate latents
267
+ condition_latent_concat = torch.cat([image_latent, condition_latent], dim=concat_dim)
268
+ # Prepare noise
269
+ latents = randn_tensor(
270
+ condition_latent_concat.shape,
271
+ generator=generator,
272
+ device=condition_latent_concat.device,
273
+ dtype=self.weight_dtype,
274
+ )
275
+ # Prepare timesteps
276
+ self.noise_scheduler.set_timesteps(num_inference_steps, device=self.device)
277
+ timesteps = self.noise_scheduler.timesteps
278
+ latents = latents * self.noise_scheduler.init_noise_sigma
279
+ # Classifier-Free Guidance
280
+ if do_classifier_free_guidance := (guidance_scale > 1.0):
281
+ condition_latent_concat = torch.cat(
282
+ [
283
+ torch.cat([image_latent, torch.zeros_like(condition_latent)], dim=concat_dim),
284
+ condition_latent_concat,
285
+ ]
286
+ )
287
+
288
+ # Denoising loop
289
+ extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
290
+ num_warmup_steps = (len(timesteps) - num_inference_steps * self.noise_scheduler.order)
291
+ with tqdm.tqdm(total=num_inference_steps) as progress_bar:
292
+ for i, t in enumerate(timesteps):
293
+ # expand the latents if we are doing classifier free guidance
294
+ latent_model_input = (torch.cat([latents] * 2) if do_classifier_free_guidance else latents)
295
+ latent_model_input = self.noise_scheduler.scale_model_input(latent_model_input, t)
296
+ # prepare the input for the inpainting model
297
+ p2p_latent_model_input = torch.cat([latent_model_input, condition_latent_concat], dim=1)
298
+ # predict the noise residual
299
+ noise_pred= self.unet(
300
+ p2p_latent_model_input,
301
+ t.to(self.device),
302
+ encoder_hidden_states=None,
303
+ return_dict=False,
304
+ )[0]
305
+ # perform guidance
306
+ if do_classifier_free_guidance:
307
+ noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
308
+ noise_pred = noise_pred_uncond + guidance_scale * (
309
+ noise_pred_text - noise_pred_uncond
310
+ )
311
+ # compute the previous noisy sample x_t -> x_t-1
312
+ latents = self.noise_scheduler.step(
313
+ noise_pred, t, latents, **extra_step_kwargs
314
+ ).prev_sample
315
+ # call the callback, if provided
316
+ if i == len(timesteps) - 1 or (
317
+ (i + 1) > num_warmup_steps
318
+ and (i + 1) % self.noise_scheduler.order == 0
319
+ ):
320
+ progress_bar.update()
321
+
322
+ # Decode the final latents
323
+ latents = latents.split(latents.shape[concat_dim] // 2, dim=concat_dim)[0]
324
+ latents = 1 / self.vae.config.scaling_factor * latents
325
+ image = self.vae.decode(latents.to(self.device, dtype=self.weight_dtype)).sample
326
+ image = (image / 2 + 0.5).clamp(0, 1)
327
+ # we always cast to float32 as this does not cause significant overhead and is compatible with bfloat16
328
+ image = image.cpu().permute(0, 2, 3, 1).float().numpy()
329
+ image = numpy_to_pil(image)
330
+
331
+ # Safety Check
332
+ if not self.skip_safety_check:
333
+ current_script_directory = os.path.dirname(os.path.realpath(__file__))
334
+ nsfw_image_path = os.path.join(
335
+ os.path.dirname(current_script_directory), "resource", "img", "NSFW.jpg"
336
+ )
337
+
338
+ image_np = np.array(image)
339
+ _, has_nsfw_concept = self.run_safety_checker(image=image_np)
340
+
341
+ nsfw_image = None
342
+ if os.path.exists(nsfw_image_path):
343
+ nsfw_image = PIL.Image.open(nsfw_image_path).resize(image[0].size)
344
+
345
+ for i, not_safe in enumerate(has_nsfw_concept):
346
+ if not_safe and nsfw_image is not None:
347
+ image[i] = nsfw_image
348
+ return image
CatVTON/model/pipeline.py CHANGED
@@ -205,14 +205,23 @@ class CatVTONPipeline:
205
  # Safety Check
206
  if not self.skip_safety_check:
207
  current_script_directory = os.path.dirname(os.path.realpath(__file__))
208
- nsfw_image = os.path.join(os.path.dirname(current_script_directory), 'resource', 'img', 'NSFW.jpg')
209
- nsfw_image = PIL.Image.open(nsfw_image).resize(image[0].size)
 
 
210
  image_np = np.array(image)
211
  _, has_nsfw_concept = self.run_safety_checker(image=image_np)
 
 
 
 
 
 
 
212
  for i, not_safe in enumerate(has_nsfw_concept):
213
- if not_safe:
214
  image[i] = nsfw_image
215
- return image
216
 
217
 
218
  class CatVTONPix2PixPipeline(CatVTONPipeline):
@@ -322,11 +331,18 @@ class CatVTONPix2PixPipeline(CatVTONPipeline):
322
  # Safety Check
323
  if not self.skip_safety_check:
324
  current_script_directory = os.path.dirname(os.path.realpath(__file__))
325
- nsfw_image = os.path.join(os.path.dirname(current_script_directory), 'resource', 'img', 'NSFW.jpg')
326
- nsfw_image = PIL.Image.open(nsfw_image).resize(image[0].size)
 
 
327
  image_np = np.array(image)
328
  _, has_nsfw_concept = self.run_safety_checker(image=image_np)
 
 
 
 
 
329
  for i, not_safe in enumerate(has_nsfw_concept):
330
- if not_safe:
331
  image[i] = nsfw_image
332
  return image
 
205
  # Safety Check
206
  if not self.skip_safety_check:
207
  current_script_directory = os.path.dirname(os.path.realpath(__file__))
208
+ nsfw_image_path = os.path.join(
209
+ os.path.dirname(current_script_directory), "resource", "img", "NSFW.jpg"
210
+ )
211
+
212
  image_np = np.array(image)
213
  _, has_nsfw_concept = self.run_safety_checker(image=image_np)
214
+
215
+ # Deployed HF Spaces may not include the placeholder NSFW image.
216
+ # If missing, skip replacement but still return the generated result.
217
+ nsfw_image = None
218
+ if os.path.exists(nsfw_image_path):
219
+ nsfw_image = PIL.Image.open(nsfw_image_path).resize(image[0].size)
220
+
221
  for i, not_safe in enumerate(has_nsfw_concept):
222
+ if not_safe and nsfw_image is not None:
223
  image[i] = nsfw_image
224
+ return image
225
 
226
 
227
  class CatVTONPix2PixPipeline(CatVTONPipeline):
 
331
  # Safety Check
332
  if not self.skip_safety_check:
333
  current_script_directory = os.path.dirname(os.path.realpath(__file__))
334
+ nsfw_image_path = os.path.join(
335
+ os.path.dirname(current_script_directory), "resource", "img", "NSFW.jpg"
336
+ )
337
+
338
  image_np = np.array(image)
339
  _, has_nsfw_concept = self.run_safety_checker(image=image_np)
340
+
341
+ nsfw_image = None
342
+ if os.path.exists(nsfw_image_path):
343
+ nsfw_image = PIL.Image.open(nsfw_image_path).resize(image[0].size)
344
+
345
  for i, not_safe in enumerate(has_nsfw_concept):
346
+ if not_safe and nsfw_image is not None:
347
  image[i] = nsfw_image
348
  return image