InvokeAI 2.2.4.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. backend/__init__.py +0 -0
  2. backend/invoke_ai_web_server.py +1724 -0
  3. backend/modules/__init__.py +0 -0
  4. backend/modules/create_cmd_parser.py +55 -0
  5. backend/modules/get_canvas_generation_mode.py +117 -0
  6. backend/modules/parameters.py +71 -0
  7. backend/modules/parse_seed_weights.py +47 -0
  8. configs/INITIAL_MODELS.yaml +80 -0
  9. configs/models.yaml.example +29 -0
  10. configs/sd-concepts.txt +803 -0
  11. configs/stable-diffusion/v1-finetune.yaml +110 -0
  12. configs/stable-diffusion/v1-finetune_style.yaml +103 -0
  13. configs/stable-diffusion/v1-inference.yaml +79 -0
  14. configs/stable-diffusion/v1-inpainting-inference.yaml +79 -0
  15. configs/stable-diffusion/v1-m1-finetune.yaml +110 -0
  16. docs/assets/invoke-web-server-1.png +0 -0
  17. docs/assets/invoke_ai_banner.png +0 -0
  18. invokeai-2.2.4.5.data/data/assets/caution.png +0 -0
  19. invokeai-2.2.4.5.data/data/configs/INITIAL_MODELS.yaml +80 -0
  20. invokeai-2.2.4.5.data/data/configs/models.yaml.example +29 -0
  21. invokeai-2.2.4.5.data/data/configs/sd-concepts.txt +803 -0
  22. invokeai-2.2.4.5.data/data/configs/stable-diffusion/v1-finetune.yaml +110 -0
  23. invokeai-2.2.4.5.data/data/configs/stable-diffusion/v1-finetune_style.yaml +103 -0
  24. invokeai-2.2.4.5.data/data/configs/stable-diffusion/v1-inference.yaml +79 -0
  25. invokeai-2.2.4.5.data/data/configs/stable-diffusion/v1-inpainting-inference.yaml +79 -0
  26. invokeai-2.2.4.5.data/data/configs/stable-diffusion/v1-m1-finetune.yaml +110 -0
  27. invokeai-2.2.4.5.data/data/frontend/dist/assets/Inter-Bold.790c108b.ttf +0 -0
  28. invokeai-2.2.4.5.data/data/frontend/dist/assets/Inter.b9a8e5e2.ttf +0 -0
  29. invokeai-2.2.4.5.data/data/frontend/dist/assets/favicon.0d253ced.ico +0 -0
  30. invokeai-2.2.4.5.data/data/frontend/dist/assets/index-legacy-b741077f.js +48 -0
  31. invokeai-2.2.4.5.data/data/frontend/dist/assets/index.0dadf5d0.css +1 -0
  32. invokeai-2.2.4.5.data/data/frontend/dist/assets/index.21ff81eb.js +625 -0
  33. invokeai-2.2.4.5.data/data/frontend/dist/assets/logo.13003d72.png +0 -0
  34. invokeai-2.2.4.5.data/data/frontend/dist/assets/polyfills-legacy-dde3a68a.js +4 -0
  35. invokeai-2.2.4.5.data/data/frontend/dist/assets/polyfills.1ff60148.js +1 -0
  36. invokeai-2.2.4.5.data/data/frontend/dist/index.html +23 -0
  37. invokeai-2.2.4.5.data/data/scripts/configure_invokeai.py +797 -0
  38. invokeai-2.2.4.5.data/data/scripts/create_root.py +43 -0
  39. invokeai-2.2.4.5.data/data/scripts/dream.py +10 -0
  40. invokeai-2.2.4.5.data/data/scripts/images2prompt.py +30 -0
  41. invokeai-2.2.4.5.data/data/scripts/invoke.py +5 -0
  42. invokeai-2.2.4.5.data/data/scripts/legacy_api.py +681 -0
  43. invokeai-2.2.4.5.data/data/scripts/load_models.py +12 -0
  44. invokeai-2.2.4.5.data/data/scripts/merge_embeddings.py +124 -0
  45. invokeai-2.2.4.5.data/data/scripts/preload_models.py +12 -0
  46. invokeai-2.2.4.5.data/data/scripts/sd-metadata.py +22 -0
  47. invokeai-2.2.4.5.dist-info/LICENSE +21 -0
  48. invokeai-2.2.4.5.dist-info/LICENSE-ModelWeights.txt +294 -0
  49. invokeai-2.2.4.5.dist-info/METADATA +288 -0
  50. invokeai-2.2.4.5.dist-info/RECORD +169 -0
  51. invokeai-2.2.4.5.dist-info/WHEEL +5 -0
  52. invokeai-2.2.4.5.dist-info/entry_points.txt +4 -0
  53. invokeai-2.2.4.5.dist-info/top_level.txt +6 -0
  54. invokeai-2.2.4.5.dist-info/zip-safe +1 -0
  55. ldm/__init__.py +0 -0
  56. ldm/data/__init__.py +0 -0
  57. ldm/data/base.py +31 -0
  58. ldm/data/imagenet.py +466 -0
  59. ldm/data/lsun.py +126 -0
  60. ldm/data/personalized.py +202 -0
  61. ldm/data/personalized_style.py +169 -0
  62. ldm/generate.py +1159 -0
  63. ldm/invoke/CLI.py +960 -0
  64. ldm/invoke/__init__.py +3 -0
  65. ldm/invoke/args.py +1257 -0
  66. ldm/invoke/concepts_lib.py +169 -0
  67. ldm/invoke/conditioning.py +289 -0
  68. ldm/invoke/devices.py +27 -0
  69. ldm/invoke/generator/__init__.py +4 -0
  70. ldm/invoke/generator/base.py +335 -0
  71. ldm/invoke/generator/embiggen.py +501 -0
  72. ldm/invoke/generator/img2img.py +97 -0
  73. ldm/invoke/generator/inpaint.py +358 -0
  74. ldm/invoke/generator/omnibus.py +175 -0
  75. ldm/invoke/generator/txt2img.py +88 -0
  76. ldm/invoke/generator/txt2img2img.py +182 -0
  77. ldm/invoke/globals.py +31 -0
  78. ldm/invoke/image_util.py +70 -0
  79. ldm/invoke/log.py +66 -0
  80. ldm/invoke/model_cache.py +449 -0
  81. ldm/invoke/patchmatch.py +44 -0
  82. ldm/invoke/pngwriter.py +113 -0
  83. ldm/invoke/prompt_parser.py +655 -0
  84. ldm/invoke/readline.py +442 -0
  85. ldm/invoke/restoration/__init__.py +4 -0
  86. ldm/invoke/restoration/base.py +38 -0
  87. ldm/invoke/restoration/codeformer.py +103 -0
  88. ldm/invoke/restoration/codeformer_arch.py +276 -0
  89. ldm/invoke/restoration/gfpgan.py +84 -0
  90. ldm/invoke/restoration/outcrop.py +108 -0
  91. ldm/invoke/restoration/outpaint.py +92 -0
  92. ldm/invoke/restoration/realesrgan.py +90 -0
  93. ldm/invoke/restoration/vqgan_arch.py +435 -0
  94. ldm/invoke/seamless.py +30 -0
  95. ldm/invoke/server.py +282 -0
  96. ldm/invoke/server_legacy.py +246 -0
  97. ldm/invoke/txt2mask.py +136 -0
  98. ldm/lr_scheduler.py +143 -0
  99. ldm/models/__init__.py +0 -0
  100. ldm/models/autoencoder.py +596 -0
  101. ldm/models/diffusion/__init__.py +0 -0
  102. ldm/models/diffusion/classifier.py +355 -0
  103. ldm/models/diffusion/cross_attention_control.py +443 -0
  104. ldm/models/diffusion/cross_attention_map_saving.py +95 -0
  105. ldm/models/diffusion/ddim.py +111 -0
  106. ldm/models/diffusion/ddpm.py +2263 -0
  107. ldm/models/diffusion/ksampler.py +312 -0
  108. ldm/models/diffusion/plms.py +146 -0
  109. ldm/models/diffusion/sampler.py +450 -0
  110. ldm/models/diffusion/shared_invokeai_diffusion.py +244 -0
  111. ldm/modules/__init__.py +0 -0
  112. ldm/modules/attention.py +274 -0
  113. ldm/modules/diffusionmodules/__init__.py +0 -0
  114. ldm/modules/diffusionmodules/model.py +894 -0
  115. ldm/modules/diffusionmodules/openaimodel.py +1036 -0
  116. ldm/modules/diffusionmodules/util.py +314 -0
  117. ldm/modules/distributions/__init__.py +0 -0
  118. ldm/modules/distributions/distributions.py +104 -0
  119. ldm/modules/ema.py +88 -0
  120. ldm/modules/embedding_manager.py +413 -0
  121. ldm/modules/encoders/__init__.py +0 -0
  122. ldm/modules/encoders/modules.py +772 -0
  123. ldm/modules/image_degradation/__init__.py +6 -0
  124. ldm/modules/image_degradation/bsrgan.py +846 -0
  125. ldm/modules/image_degradation/bsrgan_light.py +751 -0
  126. ldm/modules/image_degradation/utils_image.py +1024 -0
  127. ldm/modules/losses/__init__.py +1 -0
  128. ldm/modules/losses/contperceptual.py +175 -0
  129. ldm/modules/losses/vqperceptual.py +240 -0
  130. ldm/modules/x_transformer.py +752 -0
  131. ldm/simplet2i.py +13 -0
  132. ldm/util.py +264 -0
  133. scripts/configure_invokeai.py +797 -0
  134. scripts/create_root.py +43 -0
  135. scripts/dream.py +10 -0
  136. scripts/images2prompt.py +30 -0
  137. scripts/invoke.py +5 -0
  138. scripts/legacy_api.py +681 -0
  139. scripts/load_models.py +12 -0
  140. scripts/merge_embeddings.py +124 -0
  141. scripts/orig_scripts/img2img.py +296 -0
  142. scripts/orig_scripts/inpaint.py +98 -0
  143. scripts/orig_scripts/knn2img.py +397 -0
  144. scripts/orig_scripts/sample_diffusion.py +313 -0
  145. scripts/orig_scripts/train_searcher.py +147 -0
  146. scripts/orig_scripts/txt2img.py +324 -0
  147. scripts/preload_models.py +12 -0
  148. scripts/sd-metadata.py +22 -0
  149. vendored/clip/clip/__init__.py +1 -0
  150. vendored/clip/clip/bpe_simple_vocab_16e6.txt.gz +0 -0
  151. vendored/clip/clip/clip.py +237 -0
  152. vendored/clip/clip/model.py +436 -0
  153. vendored/clip/clip/simple_tokenizer.py +132 -0
  154. vendored/clip/hubconf.py +42 -0
  155. vendored/clip/setup.py +21 -0
  156. vendored/clip/tests/test_consistency.py +25 -0
  157. vendored/clipseg/datasets/coco_wrapper.py +99 -0
  158. vendored/clipseg/datasets/pascal_zeroshot.py +60 -0
  159. vendored/clipseg/datasets/pfe_dataset.py +129 -0
  160. vendored/clipseg/datasets/phrasecut.py +380 -0
  161. vendored/clipseg/datasets/utils.py +68 -0
  162. vendored/clipseg/evaluation_utils.py +292 -0
  163. vendored/clipseg/general_utils.py +272 -0
  164. vendored/clipseg/metrics.py +271 -0
  165. vendored/clipseg/models/clipseg.py +538 -0
  166. vendored/clipseg/models/vitseg.py +286 -0
  167. vendored/clipseg/score.py +453 -0
  168. vendored/clipseg/setup.py +30 -0
  169. vendored/clipseg/training.py +266 -0
@@ -0,0 +1,335 @@
1
+ '''
2
+ Base class for ldm.invoke.generator.*
3
+ including img2img, txt2img, and inpaint
4
+ '''
5
+ import torch
6
+ import numpy as np
7
+ import random
8
+ import os
9
+ import os.path as osp
10
+ import traceback
11
+ from tqdm import tqdm, trange
12
+ from PIL import Image, ImageFilter, ImageChops
13
+ import cv2 as cv
14
+ from einops import rearrange, repeat
15
+ from pytorch_lightning import seed_everything
16
+ from ldm.invoke.devices import choose_autocast
17
+ from ldm.models.diffusion.cross_attention_map_saving import AttentionMapSaver
18
+ from ldm.util import rand_perlin_2d
19
+
20
+ downsampling = 8
21
+ CAUTION_IMG = 'assets/caution.png'
22
+
23
+ class Generator():
24
+ def __init__(self, model, precision):
25
+ self.model = model
26
+ self.precision = precision
27
+ self.seed = None
28
+ self.latent_channels = model.channels
29
+ self.downsampling_factor = downsampling # BUG: should come from model or config
30
+ self.safety_checker = None
31
+ self.perlin = 0.0
32
+ self.threshold = 0
33
+ self.variation_amount = 0
34
+ self.with_variations = []
35
+ self.use_mps_noise = False
36
+ self.free_gpu_mem = None
37
+ self.caution_img = None
38
+
39
+ # this is going to be overridden in img2img.py, txt2img.py and inpaint.py
40
+ def get_make_image(self,prompt,**kwargs):
41
+ """
42
+ Returns a function returning an image derived from the prompt and the initial image
43
+ Return value depends on the seed at the time you call it
44
+ """
45
+ raise NotImplementedError("image_iterator() must be implemented in a descendent class")
46
+
47
+ def set_variation(self, seed, variation_amount, with_variations):
48
+ self.seed = seed
49
+ self.variation_amount = variation_amount
50
+ self.with_variations = with_variations
51
+
52
+ def generate(self,prompt,init_image,width,height,sampler, iterations=1,seed=None,
53
+ image_callback=None, step_callback=None, threshold=0.0, perlin=0.0,
54
+ safety_checker:dict=None,
55
+ attention_maps_callback = None,
56
+ **kwargs):
57
+ scope = choose_autocast(self.precision)
58
+ self.safety_checker = safety_checker
59
+ attention_maps_images = []
60
+ attention_maps_callback = lambda saver: attention_maps_images.append(saver.get_stacked_maps_image())
61
+ make_image = self.get_make_image(
62
+ prompt,
63
+ sampler = sampler,
64
+ init_image = init_image,
65
+ width = width,
66
+ height = height,
67
+ step_callback = step_callback,
68
+ threshold = threshold,
69
+ perlin = perlin,
70
+ attention_maps_callback = attention_maps_callback,
71
+ **kwargs
72
+ )
73
+ results = []
74
+ seed = seed if seed is not None and seed >= 0 else self.new_seed()
75
+ first_seed = seed
76
+ seed, initial_noise = self.generate_initial_noise(seed, width, height)
77
+
78
+ # There used to be an additional self.model.ema_scope() here, but it breaks
79
+ # the inpaint-1.5 model. Not sure what it did.... ?
80
+ with scope(self.model.device.type):
81
+ for n in trange(iterations, desc='Generating'):
82
+ x_T = None
83
+ if self.variation_amount > 0:
84
+ seed_everything(seed)
85
+ target_noise = self.get_noise(width,height)
86
+ x_T = self.slerp(self.variation_amount, initial_noise, target_noise)
87
+ elif initial_noise is not None:
88
+ # i.e. we specified particular variations
89
+ x_T = initial_noise
90
+ else:
91
+ seed_everything(seed)
92
+ try:
93
+ x_T = self.get_noise(width,height)
94
+ except:
95
+ print('** An error occurred while getting initial noise **')
96
+ print(traceback.format_exc())
97
+
98
+ image = make_image(x_T)
99
+
100
+ if self.safety_checker is not None:
101
+ image = self.safety_check(image)
102
+
103
+ results.append([image, seed])
104
+
105
+ if image_callback is not None:
106
+ attention_maps_image = None if len(attention_maps_images)==0 else attention_maps_images[-1]
107
+ image_callback(image, seed, first_seed=first_seed, attention_maps_image=attention_maps_image)
108
+
109
+ seed = self.new_seed()
110
+
111
+ return results
112
+
113
+ def sample_to_image(self,samples)->Image.Image:
114
+ """
115
+ Given samples returned from a sampler, converts
116
+ it into a PIL Image
117
+ """
118
+ x_samples = self.model.decode_first_stage(samples)
119
+ x_samples = torch.clamp((x_samples + 1.0) / 2.0, min=0.0, max=1.0)
120
+ if len(x_samples) != 1:
121
+ raise Exception(
122
+ f'>> expected to get a single image, but got {len(x_samples)}')
123
+ x_sample = 255.0 * rearrange(
124
+ x_samples[0].cpu().numpy(), 'c h w -> h w c'
125
+ )
126
+ return Image.fromarray(x_sample.astype(np.uint8))
127
+
128
+ # write an approximate RGB image from latent samples for a single step to PNG
129
+
130
+ def repaste_and_color_correct(self, result: Image.Image, init_image: Image.Image, init_mask: Image.Image, mask_blur_radius: int = 8) -> Image.Image:
131
+ if init_image is None or init_mask is None:
132
+ return result
133
+
134
+ # Get the original alpha channel of the mask if there is one.
135
+ # Otherwise it is some other black/white image format ('1', 'L' or 'RGB')
136
+ pil_init_mask = init_mask.getchannel('A') if init_mask.mode == 'RGBA' else init_mask.convert('L')
137
+ pil_init_image = init_image.convert('RGBA') # Add an alpha channel if one doesn't exist
138
+
139
+ # Build an image with only visible pixels from source to use as reference for color-matching.
140
+ init_rgb_pixels = np.asarray(init_image.convert('RGB'), dtype=np.uint8)
141
+ init_a_pixels = np.asarray(pil_init_image.getchannel('A'), dtype=np.uint8)
142
+ init_mask_pixels = np.asarray(pil_init_mask, dtype=np.uint8)
143
+
144
+ # Get numpy version of result
145
+ np_image = np.asarray(result, dtype=np.uint8)
146
+
147
+ # Mask and calculate mean and standard deviation
148
+ mask_pixels = init_a_pixels * init_mask_pixels > 0
149
+ np_init_rgb_pixels_masked = init_rgb_pixels[mask_pixels, :]
150
+ np_image_masked = np_image[mask_pixels, :]
151
+
152
+ if np_init_rgb_pixels_masked.size > 0:
153
+ init_means = np_init_rgb_pixels_masked.mean(axis=0)
154
+ init_std = np_init_rgb_pixels_masked.std(axis=0)
155
+ gen_means = np_image_masked.mean(axis=0)
156
+ gen_std = np_image_masked.std(axis=0)
157
+
158
+ # Color correct
159
+ np_matched_result = np_image.copy()
160
+ np_matched_result[:,:,:] = (((np_matched_result[:,:,:].astype(np.float32) - gen_means[None,None,:]) / gen_std[None,None,:]) * init_std[None,None,:] + init_means[None,None,:]).clip(0, 255).astype(np.uint8)
161
+ matched_result = Image.fromarray(np_matched_result, mode='RGB')
162
+ else:
163
+ matched_result = Image.fromarray(np_image, mode='RGB')
164
+
165
+ # Blur the mask out (into init image) by specified amount
166
+ if mask_blur_radius > 0:
167
+ nm = np.asarray(pil_init_mask, dtype=np.uint8)
168
+ nmd = cv.erode(nm, kernel=np.ones((3,3), dtype=np.uint8), iterations=int(mask_blur_radius / 2))
169
+ pmd = Image.fromarray(nmd, mode='L')
170
+ blurred_init_mask = pmd.filter(ImageFilter.BoxBlur(mask_blur_radius))
171
+ else:
172
+ blurred_init_mask = pil_init_mask
173
+
174
+ multiplied_blurred_init_mask = ImageChops.multiply(blurred_init_mask, self.pil_image.split()[-1])
175
+
176
+ # Paste original on color-corrected generation (using blurred mask)
177
+ matched_result.paste(init_image, (0,0), mask = multiplied_blurred_init_mask)
178
+ return matched_result
179
+
180
+
181
+
182
+ def sample_to_lowres_estimated_image(self,samples):
183
+ # origingally adapted from code by @erucipe and @keturn here:
184
+ # https://discuss.huggingface.co/t/decoding-latents-to-rgb-without-upscaling/23204/7
185
+
186
+ # these updated numbers for v1.5 are from @torridgristle
187
+ v1_5_latent_rgb_factors = torch.tensor([
188
+ # R G B
189
+ [ 0.3444, 0.1385, 0.0670], # L1
190
+ [ 0.1247, 0.4027, 0.1494], # L2
191
+ [-0.3192, 0.2513, 0.2103], # L3
192
+ [-0.1307, -0.1874, -0.7445] # L4
193
+ ], dtype=samples.dtype, device=samples.device)
194
+
195
+ latent_image = samples[0].permute(1, 2, 0) @ v1_5_latent_rgb_factors
196
+ latents_ubyte = (((latent_image + 1) / 2)
197
+ .clamp(0, 1) # change scale from -1..1 to 0..1
198
+ .mul(0xFF) # to 0..255
199
+ .byte()).cpu()
200
+
201
+ return Image.fromarray(latents_ubyte.numpy())
202
+
203
+ def generate_initial_noise(self, seed, width, height):
204
+ initial_noise = None
205
+ if self.variation_amount > 0 or len(self.with_variations) > 0:
206
+ # use fixed initial noise plus random noise per iteration
207
+ seed_everything(seed)
208
+ initial_noise = self.get_noise(width,height)
209
+ for v_seed, v_weight in self.with_variations:
210
+ seed = v_seed
211
+ seed_everything(seed)
212
+ next_noise = self.get_noise(width,height)
213
+ initial_noise = self.slerp(v_weight, initial_noise, next_noise)
214
+ if self.variation_amount > 0:
215
+ random.seed() # reset RNG to an actually random state, so we can get a random seed for variations
216
+ seed = random.randrange(0,np.iinfo(np.uint32).max)
217
+ return (seed, initial_noise)
218
+ else:
219
+ return (seed, None)
220
+
221
+ # returns a tensor filled with random numbers from a normal distribution
222
+ def get_noise(self,width,height):
223
+ """
224
+ Returns a tensor filled with random numbers, either form a normal distribution
225
+ (txt2img) or from the latent image (img2img, inpaint)
226
+ """
227
+ raise NotImplementedError("get_noise() must be implemented in a descendent class")
228
+
229
+ def get_perlin_noise(self,width,height):
230
+ fixdevice = 'cpu' if (self.model.device.type == 'mps') else self.model.device
231
+ return torch.stack([rand_perlin_2d((height, width), (8, 8), device = self.model.device).to(fixdevice) for _ in range(self.latent_channels)], dim=0).to(self.model.device)
232
+
233
+ def new_seed(self):
234
+ self.seed = random.randrange(0, np.iinfo(np.uint32).max)
235
+ return self.seed
236
+
237
+ def slerp(self, t, v0, v1, DOT_THRESHOLD=0.9995):
238
+ '''
239
+ Spherical linear interpolation
240
+ Args:
241
+ t (float/np.ndarray): Float value between 0.0 and 1.0
242
+ v0 (np.ndarray): Starting vector
243
+ v1 (np.ndarray): Final vector
244
+ DOT_THRESHOLD (float): Threshold for considering the two vectors as
245
+ colineal. Not recommended to alter this.
246
+ Returns:
247
+ v2 (np.ndarray): Interpolation vector between v0 and v1
248
+ '''
249
+ inputs_are_torch = False
250
+ if not isinstance(v0, np.ndarray):
251
+ inputs_are_torch = True
252
+ v0 = v0.detach().cpu().numpy()
253
+ if not isinstance(v1, np.ndarray):
254
+ inputs_are_torch = True
255
+ v1 = v1.detach().cpu().numpy()
256
+
257
+ dot = np.sum(v0 * v1 / (np.linalg.norm(v0) * np.linalg.norm(v1)))
258
+ if np.abs(dot) > DOT_THRESHOLD:
259
+ v2 = (1 - t) * v0 + t * v1
260
+ else:
261
+ theta_0 = np.arccos(dot)
262
+ sin_theta_0 = np.sin(theta_0)
263
+ theta_t = theta_0 * t
264
+ sin_theta_t = np.sin(theta_t)
265
+ s0 = np.sin(theta_0 - theta_t) / sin_theta_0
266
+ s1 = sin_theta_t / sin_theta_0
267
+ v2 = s0 * v0 + s1 * v1
268
+
269
+ if inputs_are_torch:
270
+ v2 = torch.from_numpy(v2).to(self.model.device)
271
+
272
+ return v2
273
+
274
+ def safety_check(self,image:Image.Image):
275
+ '''
276
+ If the CompViz safety checker flags an NSFW image, we
277
+ blur it out.
278
+ '''
279
+ import diffusers
280
+
281
+ checker = self.safety_checker['checker']
282
+ extractor = self.safety_checker['extractor']
283
+ features = extractor([image], return_tensors="pt")
284
+ features.to(self.model.device)
285
+
286
+ # unfortunately checker requires the numpy version, so we have to convert back
287
+ x_image = np.array(image).astype(np.float32) / 255.0
288
+ x_image = x_image[None].transpose(0, 3, 1, 2)
289
+
290
+ diffusers.logging.set_verbosity_error()
291
+ checked_image, has_nsfw_concept = checker(images=x_image, clip_input=features.pixel_values)
292
+ if has_nsfw_concept[0]:
293
+ print('** An image with potential non-safe content has been detected. A blurred image will be returned. **')
294
+ return self.blur(image)
295
+ else:
296
+ return image
297
+
298
+ def blur(self,input):
299
+ blurry = input.filter(filter=ImageFilter.GaussianBlur(radius=32))
300
+ try:
301
+ caution = self.get_caution_img()
302
+ if caution:
303
+ blurry.paste(caution,(0,0),caution)
304
+ except FileNotFoundError:
305
+ pass
306
+ return blurry
307
+
308
+ def get_caution_img(self):
309
+ path = None
310
+ if self.caution_img:
311
+ return self.caution_img
312
+ # Find the caution image. If we are installed in the package directory it will
313
+ # be six levels up. If we are in the repo directory it will be three levels up.
314
+ for dots in ('../../..','../../../../../..'):
315
+ caution_path = osp.join(osp.dirname(__file__),dots,CAUTION_IMG)
316
+ if osp.exists(caution_path):
317
+ path = caution_path
318
+ break
319
+ if not path:
320
+ return
321
+ caution = Image.open(path)
322
+ self.caution_img = caution.resize((caution.width // 2, caution.height //2))
323
+ return self.caution_img
324
+
325
+ # this is a handy routine for debugging use. Given a generated sample,
326
+ # convert it into a PNG image and store it at the indicated path
327
+ def save_sample(self, sample, filepath):
328
+ image = self.sample_to_image(sample)
329
+ dirname = os.path.dirname(filepath) or '.'
330
+ if not os.path.exists(dirname):
331
+ print(f'** creating directory {dirname}')
332
+ os.makedirs(dirname, exist_ok=True)
333
+ image.save(filepath,'PNG')
334
+
335
+