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.
- backend/__init__.py +0 -0
- backend/invoke_ai_web_server.py +1724 -0
- backend/modules/__init__.py +0 -0
- backend/modules/create_cmd_parser.py +55 -0
- backend/modules/get_canvas_generation_mode.py +117 -0
- backend/modules/parameters.py +71 -0
- backend/modules/parse_seed_weights.py +47 -0
- configs/INITIAL_MODELS.yaml +80 -0
- configs/models.yaml.example +29 -0
- configs/sd-concepts.txt +803 -0
- configs/stable-diffusion/v1-finetune.yaml +110 -0
- configs/stable-diffusion/v1-finetune_style.yaml +103 -0
- configs/stable-diffusion/v1-inference.yaml +79 -0
- configs/stable-diffusion/v1-inpainting-inference.yaml +79 -0
- configs/stable-diffusion/v1-m1-finetune.yaml +110 -0
- docs/assets/invoke-web-server-1.png +0 -0
- docs/assets/invoke_ai_banner.png +0 -0
- invokeai-2.2.4.5.data/data/assets/caution.png +0 -0
- invokeai-2.2.4.5.data/data/configs/INITIAL_MODELS.yaml +80 -0
- invokeai-2.2.4.5.data/data/configs/models.yaml.example +29 -0
- invokeai-2.2.4.5.data/data/configs/sd-concepts.txt +803 -0
- invokeai-2.2.4.5.data/data/configs/stable-diffusion/v1-finetune.yaml +110 -0
- invokeai-2.2.4.5.data/data/configs/stable-diffusion/v1-finetune_style.yaml +103 -0
- invokeai-2.2.4.5.data/data/configs/stable-diffusion/v1-inference.yaml +79 -0
- invokeai-2.2.4.5.data/data/configs/stable-diffusion/v1-inpainting-inference.yaml +79 -0
- invokeai-2.2.4.5.data/data/configs/stable-diffusion/v1-m1-finetune.yaml +110 -0
- invokeai-2.2.4.5.data/data/frontend/dist/assets/Inter-Bold.790c108b.ttf +0 -0
- invokeai-2.2.4.5.data/data/frontend/dist/assets/Inter.b9a8e5e2.ttf +0 -0
- invokeai-2.2.4.5.data/data/frontend/dist/assets/favicon.0d253ced.ico +0 -0
- invokeai-2.2.4.5.data/data/frontend/dist/assets/index-legacy-b741077f.js +48 -0
- invokeai-2.2.4.5.data/data/frontend/dist/assets/index.0dadf5d0.css +1 -0
- invokeai-2.2.4.5.data/data/frontend/dist/assets/index.21ff81eb.js +625 -0
- invokeai-2.2.4.5.data/data/frontend/dist/assets/logo.13003d72.png +0 -0
- invokeai-2.2.4.5.data/data/frontend/dist/assets/polyfills-legacy-dde3a68a.js +4 -0
- invokeai-2.2.4.5.data/data/frontend/dist/assets/polyfills.1ff60148.js +1 -0
- invokeai-2.2.4.5.data/data/frontend/dist/index.html +23 -0
- invokeai-2.2.4.5.data/data/scripts/configure_invokeai.py +797 -0
- invokeai-2.2.4.5.data/data/scripts/create_root.py +43 -0
- invokeai-2.2.4.5.data/data/scripts/dream.py +10 -0
- invokeai-2.2.4.5.data/data/scripts/images2prompt.py +30 -0
- invokeai-2.2.4.5.data/data/scripts/invoke.py +5 -0
- invokeai-2.2.4.5.data/data/scripts/legacy_api.py +681 -0
- invokeai-2.2.4.5.data/data/scripts/load_models.py +12 -0
- invokeai-2.2.4.5.data/data/scripts/merge_embeddings.py +124 -0
- invokeai-2.2.4.5.data/data/scripts/preload_models.py +12 -0
- invokeai-2.2.4.5.data/data/scripts/sd-metadata.py +22 -0
- invokeai-2.2.4.5.dist-info/LICENSE +21 -0
- invokeai-2.2.4.5.dist-info/LICENSE-ModelWeights.txt +294 -0
- invokeai-2.2.4.5.dist-info/METADATA +288 -0
- invokeai-2.2.4.5.dist-info/RECORD +169 -0
- invokeai-2.2.4.5.dist-info/WHEEL +5 -0
- invokeai-2.2.4.5.dist-info/entry_points.txt +4 -0
- invokeai-2.2.4.5.dist-info/top_level.txt +6 -0
- invokeai-2.2.4.5.dist-info/zip-safe +1 -0
- ldm/__init__.py +0 -0
- ldm/data/__init__.py +0 -0
- ldm/data/base.py +31 -0
- ldm/data/imagenet.py +466 -0
- ldm/data/lsun.py +126 -0
- ldm/data/personalized.py +202 -0
- ldm/data/personalized_style.py +169 -0
- ldm/generate.py +1159 -0
- ldm/invoke/CLI.py +960 -0
- ldm/invoke/__init__.py +3 -0
- ldm/invoke/args.py +1257 -0
- ldm/invoke/concepts_lib.py +169 -0
- ldm/invoke/conditioning.py +289 -0
- ldm/invoke/devices.py +27 -0
- ldm/invoke/generator/__init__.py +4 -0
- ldm/invoke/generator/base.py +335 -0
- ldm/invoke/generator/embiggen.py +501 -0
- ldm/invoke/generator/img2img.py +97 -0
- ldm/invoke/generator/inpaint.py +358 -0
- ldm/invoke/generator/omnibus.py +175 -0
- ldm/invoke/generator/txt2img.py +88 -0
- ldm/invoke/generator/txt2img2img.py +182 -0
- ldm/invoke/globals.py +31 -0
- ldm/invoke/image_util.py +70 -0
- ldm/invoke/log.py +66 -0
- ldm/invoke/model_cache.py +449 -0
- ldm/invoke/patchmatch.py +44 -0
- ldm/invoke/pngwriter.py +113 -0
- ldm/invoke/prompt_parser.py +655 -0
- ldm/invoke/readline.py +442 -0
- ldm/invoke/restoration/__init__.py +4 -0
- ldm/invoke/restoration/base.py +38 -0
- ldm/invoke/restoration/codeformer.py +103 -0
- ldm/invoke/restoration/codeformer_arch.py +276 -0
- ldm/invoke/restoration/gfpgan.py +84 -0
- ldm/invoke/restoration/outcrop.py +108 -0
- ldm/invoke/restoration/outpaint.py +92 -0
- ldm/invoke/restoration/realesrgan.py +90 -0
- ldm/invoke/restoration/vqgan_arch.py +435 -0
- ldm/invoke/seamless.py +30 -0
- ldm/invoke/server.py +282 -0
- ldm/invoke/server_legacy.py +246 -0
- ldm/invoke/txt2mask.py +136 -0
- ldm/lr_scheduler.py +143 -0
- ldm/models/__init__.py +0 -0
- ldm/models/autoencoder.py +596 -0
- ldm/models/diffusion/__init__.py +0 -0
- ldm/models/diffusion/classifier.py +355 -0
- ldm/models/diffusion/cross_attention_control.py +443 -0
- ldm/models/diffusion/cross_attention_map_saving.py +95 -0
- ldm/models/diffusion/ddim.py +111 -0
- ldm/models/diffusion/ddpm.py +2263 -0
- ldm/models/diffusion/ksampler.py +312 -0
- ldm/models/diffusion/plms.py +146 -0
- ldm/models/diffusion/sampler.py +450 -0
- ldm/models/diffusion/shared_invokeai_diffusion.py +244 -0
- ldm/modules/__init__.py +0 -0
- ldm/modules/attention.py +274 -0
- ldm/modules/diffusionmodules/__init__.py +0 -0
- ldm/modules/diffusionmodules/model.py +894 -0
- ldm/modules/diffusionmodules/openaimodel.py +1036 -0
- ldm/modules/diffusionmodules/util.py +314 -0
- ldm/modules/distributions/__init__.py +0 -0
- ldm/modules/distributions/distributions.py +104 -0
- ldm/modules/ema.py +88 -0
- ldm/modules/embedding_manager.py +413 -0
- ldm/modules/encoders/__init__.py +0 -0
- ldm/modules/encoders/modules.py +772 -0
- ldm/modules/image_degradation/__init__.py +6 -0
- ldm/modules/image_degradation/bsrgan.py +846 -0
- ldm/modules/image_degradation/bsrgan_light.py +751 -0
- ldm/modules/image_degradation/utils_image.py +1024 -0
- ldm/modules/losses/__init__.py +1 -0
- ldm/modules/losses/contperceptual.py +175 -0
- ldm/modules/losses/vqperceptual.py +240 -0
- ldm/modules/x_transformer.py +752 -0
- ldm/simplet2i.py +13 -0
- ldm/util.py +264 -0
- scripts/configure_invokeai.py +797 -0
- scripts/create_root.py +43 -0
- scripts/dream.py +10 -0
- scripts/images2prompt.py +30 -0
- scripts/invoke.py +5 -0
- scripts/legacy_api.py +681 -0
- scripts/load_models.py +12 -0
- scripts/merge_embeddings.py +124 -0
- scripts/orig_scripts/img2img.py +296 -0
- scripts/orig_scripts/inpaint.py +98 -0
- scripts/orig_scripts/knn2img.py +397 -0
- scripts/orig_scripts/sample_diffusion.py +313 -0
- scripts/orig_scripts/train_searcher.py +147 -0
- scripts/orig_scripts/txt2img.py +324 -0
- scripts/preload_models.py +12 -0
- scripts/sd-metadata.py +22 -0
- vendored/clip/clip/__init__.py +1 -0
- vendored/clip/clip/bpe_simple_vocab_16e6.txt.gz +0 -0
- vendored/clip/clip/clip.py +237 -0
- vendored/clip/clip/model.py +436 -0
- vendored/clip/clip/simple_tokenizer.py +132 -0
- vendored/clip/hubconf.py +42 -0
- vendored/clip/setup.py +21 -0
- vendored/clip/tests/test_consistency.py +25 -0
- vendored/clipseg/datasets/coco_wrapper.py +99 -0
- vendored/clipseg/datasets/pascal_zeroshot.py +60 -0
- vendored/clipseg/datasets/pfe_dataset.py +129 -0
- vendored/clipseg/datasets/phrasecut.py +380 -0
- vendored/clipseg/datasets/utils.py +68 -0
- vendored/clipseg/evaluation_utils.py +292 -0
- vendored/clipseg/general_utils.py +272 -0
- vendored/clipseg/metrics.py +271 -0
- vendored/clipseg/models/clipseg.py +538 -0
- vendored/clipseg/models/vitseg.py +286 -0
- vendored/clipseg/score.py +453 -0
- vendored/clipseg/setup.py +30 -0
- 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
|
+
|