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,292 @@
|
|
|
1
|
+
from torch.functional import Tensor
|
|
2
|
+
from general_utils import load_model
|
|
3
|
+
from torch.utils.data import DataLoader
|
|
4
|
+
import torch
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
def denorm(img):
|
|
8
|
+
|
|
9
|
+
np_input = False
|
|
10
|
+
if isinstance(img, np.ndarray):
|
|
11
|
+
img = torch.from_numpy(img)
|
|
12
|
+
np_input = True
|
|
13
|
+
|
|
14
|
+
mean = torch.Tensor([0.485, 0.456, 0.406])
|
|
15
|
+
std = torch.Tensor([0.229, 0.224, 0.225])
|
|
16
|
+
|
|
17
|
+
img_denorm = (img*std[:,None,None]) + mean[:,None,None]
|
|
18
|
+
|
|
19
|
+
if np_input:
|
|
20
|
+
img_denorm = np.clip(img_denorm.numpy(), 0, 1)
|
|
21
|
+
else:
|
|
22
|
+
img_denorm = torch.clamp(img_denorm, 0, 1)
|
|
23
|
+
|
|
24
|
+
return img_denorm
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def norm(img):
|
|
28
|
+
mean = torch.Tensor([0.485, 0.456, 0.406])
|
|
29
|
+
std = torch.Tensor([0.229, 0.224, 0.225])
|
|
30
|
+
return (img - mean[:,None,None]) / std[:,None,None]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def fast_iou_curve(p, g):
|
|
34
|
+
|
|
35
|
+
g = g[p.sort().indices]
|
|
36
|
+
p = torch.sigmoid(p.sort().values)
|
|
37
|
+
|
|
38
|
+
scores = []
|
|
39
|
+
vals = np.linspace(0, 1, 50)
|
|
40
|
+
|
|
41
|
+
for q in vals:
|
|
42
|
+
|
|
43
|
+
n = int(len(g) * q)
|
|
44
|
+
|
|
45
|
+
valid = torch.where(p > q)[0]
|
|
46
|
+
if len(valid) > 0:
|
|
47
|
+
n = int(valid[0])
|
|
48
|
+
else:
|
|
49
|
+
n = len(g)
|
|
50
|
+
|
|
51
|
+
fn = g[:n].sum()
|
|
52
|
+
tn = n - fn
|
|
53
|
+
tp = g[n:].sum()
|
|
54
|
+
fp = len(g) - n - tp
|
|
55
|
+
|
|
56
|
+
iou = tp / (tp + fn + fp)
|
|
57
|
+
|
|
58
|
+
precision = tp / (tp + fp)
|
|
59
|
+
recall = tp / (tp + fn)
|
|
60
|
+
|
|
61
|
+
scores += [iou]
|
|
62
|
+
|
|
63
|
+
return vals, scores
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def fast_rp_curve(p, g):
|
|
67
|
+
|
|
68
|
+
g = g[p.sort().indices]
|
|
69
|
+
p = torch.sigmoid(p.sort().values)
|
|
70
|
+
|
|
71
|
+
precisions, recalls = [], []
|
|
72
|
+
vals = np.linspace(p.min(), p.max(), 250)
|
|
73
|
+
|
|
74
|
+
for q in p[::100000]:
|
|
75
|
+
|
|
76
|
+
n = int(len(g) * q)
|
|
77
|
+
|
|
78
|
+
valid = torch.where(p > q)[0]
|
|
79
|
+
if len(valid) > 0:
|
|
80
|
+
n = int(valid[0])
|
|
81
|
+
else:
|
|
82
|
+
n = len(g)
|
|
83
|
+
|
|
84
|
+
fn = g[:n].sum()
|
|
85
|
+
tn = n - fn
|
|
86
|
+
tp = g[n:].sum()
|
|
87
|
+
fp = len(g) - n - tp
|
|
88
|
+
|
|
89
|
+
iou = tp / (tp + fn + fp)
|
|
90
|
+
|
|
91
|
+
precision = tp / (tp + fp)
|
|
92
|
+
recall = tp / (tp + fn)
|
|
93
|
+
|
|
94
|
+
precisions += [precision]
|
|
95
|
+
recalls += [recall]
|
|
96
|
+
|
|
97
|
+
return recalls, precisions
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# Image processing
|
|
101
|
+
|
|
102
|
+
def img_preprocess(batch, blur=0, grayscale=False, center_context=None, rect=False, rect_color=(255,0,0), rect_width=2,
|
|
103
|
+
brightness=1.0, bg_fac=1, colorize=False, outline=False, image_size=224):
|
|
104
|
+
import cv2
|
|
105
|
+
|
|
106
|
+
rw = rect_width
|
|
107
|
+
|
|
108
|
+
out = []
|
|
109
|
+
for img, mask in zip(batch[1], batch[2]):
|
|
110
|
+
|
|
111
|
+
img = img.cpu() if isinstance(img, torch.Tensor) else torch.from_numpy(img)
|
|
112
|
+
mask = mask.cpu() if isinstance(mask, torch.Tensor) else torch.from_numpy(mask)
|
|
113
|
+
|
|
114
|
+
img *= brightness
|
|
115
|
+
img_bl = img
|
|
116
|
+
if blur > 0: # best 5
|
|
117
|
+
img_bl = torch.from_numpy(cv2.GaussianBlur(img.permute(1,2,0).numpy(), (15, 15), blur)).permute(2,0,1)
|
|
118
|
+
|
|
119
|
+
if grayscale:
|
|
120
|
+
img_bl = img_bl[1][None]
|
|
121
|
+
|
|
122
|
+
#img_inp = img_ratio*img*mask + (1-img_ratio)*img_bl
|
|
123
|
+
# img_inp = img_ratio*img*mask + (1-img_ratio)*img_bl * (1-mask)
|
|
124
|
+
img_inp = img*mask + (bg_fac) * img_bl * (1-mask)
|
|
125
|
+
|
|
126
|
+
if rect:
|
|
127
|
+
_, bbox = crop_mask(img, mask, context=0.1)
|
|
128
|
+
img_inp[:, bbox[2]: bbox[3], max(0, bbox[0]-rw):bbox[0]+rw] = torch.tensor(rect_color)[:,None,None]
|
|
129
|
+
img_inp[:, bbox[2]: bbox[3], max(0, bbox[1]-rw):bbox[1]+rw] = torch.tensor(rect_color)[:,None,None]
|
|
130
|
+
img_inp[:, max(0, bbox[2]-1): bbox[2]+rw, bbox[0]:bbox[1]] = torch.tensor(rect_color)[:,None,None]
|
|
131
|
+
img_inp[:, max(0, bbox[3]-1): bbox[3]+rw, bbox[0]:bbox[1]] = torch.tensor(rect_color)[:,None,None]
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
if center_context is not None:
|
|
135
|
+
img_inp = object_crop(img_inp, mask, context=center_context, image_size=image_size)
|
|
136
|
+
|
|
137
|
+
if colorize:
|
|
138
|
+
img_gray = denorm(img)
|
|
139
|
+
img_gray = cv2.cvtColor(img_gray.permute(1,2,0).numpy(), cv2.COLOR_RGB2GRAY)
|
|
140
|
+
img_gray = torch.stack([torch.from_numpy(img_gray)]*3)
|
|
141
|
+
img_inp = torch.tensor([1,0.2,0.2])[:,None,None] * img_gray * mask + bg_fac * img_gray * (1-mask)
|
|
142
|
+
img_inp = norm(img_inp)
|
|
143
|
+
|
|
144
|
+
if outline:
|
|
145
|
+
cont = cv2.findContours(mask.byte().numpy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
|
146
|
+
outline_img = np.zeros(mask.shape, dtype=np.uint8)
|
|
147
|
+
cv2.drawContours(outline_img, cont[0], -1, thickness=5, color=(255, 255, 255))
|
|
148
|
+
outline_img = torch.stack([torch.from_numpy(outline_img)]*3).float() / 255.
|
|
149
|
+
img_inp = torch.tensor([1,0,0])[:,None,None] * outline_img + denorm(img_inp) * (1- outline_img)
|
|
150
|
+
img_inp = norm(img_inp)
|
|
151
|
+
|
|
152
|
+
out += [img_inp]
|
|
153
|
+
|
|
154
|
+
return torch.stack(out)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def object_crop(img, mask, context=0.0, square=False, image_size=224):
|
|
158
|
+
img_crop, bbox = crop_mask(img, mask, context=context, square=square)
|
|
159
|
+
img_crop = pad_to_square(img_crop, channel_dim=0)
|
|
160
|
+
img_crop = torch.nn.functional.interpolate(img_crop.unsqueeze(0), (image_size, image_size)).squeeze(0)
|
|
161
|
+
return img_crop
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def crop_mask(img, mask, context=0.0, square=False):
|
|
165
|
+
|
|
166
|
+
assert img.shape[1:] == mask.shape
|
|
167
|
+
|
|
168
|
+
bbox = [mask.max(0).values.argmax(), mask.size(0) - mask.max(0).values.flip(0).argmax()]
|
|
169
|
+
bbox += [mask.max(1).values.argmax(), mask.size(1) - mask.max(1).values.flip(0).argmax()]
|
|
170
|
+
bbox = [int(x) for x in bbox]
|
|
171
|
+
|
|
172
|
+
width, height = (bbox[3] - bbox[2]), (bbox[1] - bbox[0])
|
|
173
|
+
|
|
174
|
+
# square mask
|
|
175
|
+
if square:
|
|
176
|
+
bbox[0] = int(max(0, bbox[0] - context * height))
|
|
177
|
+
bbox[1] = int(min(mask.size(0), bbox[1] + context * height))
|
|
178
|
+
bbox[2] = int(max(0, bbox[2] - context * width))
|
|
179
|
+
bbox[3] = int(min(mask.size(1), bbox[3] + context * width))
|
|
180
|
+
|
|
181
|
+
width, height = (bbox[3] - bbox[2]), (bbox[1] - bbox[0])
|
|
182
|
+
if height > width:
|
|
183
|
+
bbox[2] = int(max(0, (bbox[2] - 0.5*height)))
|
|
184
|
+
bbox[3] = bbox[2] + height
|
|
185
|
+
else:
|
|
186
|
+
bbox[0] = int(max(0, (bbox[0] - 0.5*width)))
|
|
187
|
+
bbox[1] = bbox[0] + width
|
|
188
|
+
else:
|
|
189
|
+
bbox[0] = int(max(0, bbox[0] - context * height))
|
|
190
|
+
bbox[1] = int(min(mask.size(0), bbox[1] + context * height))
|
|
191
|
+
bbox[2] = int(max(0, bbox[2] - context * width))
|
|
192
|
+
bbox[3] = int(min(mask.size(1), bbox[3] + context * width))
|
|
193
|
+
|
|
194
|
+
width, height = (bbox[3] - bbox[2]), (bbox[1] - bbox[0])
|
|
195
|
+
img_crop = img[:, bbox[2]: bbox[3], bbox[0]: bbox[1]]
|
|
196
|
+
return img_crop, bbox
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def pad_to_square(img, channel_dim=2, fill=0):
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
add padding such that a squared image is returned """
|
|
204
|
+
|
|
205
|
+
from torchvision.transforms.functional import pad
|
|
206
|
+
|
|
207
|
+
if channel_dim == 2:
|
|
208
|
+
img = img.permute(2, 0, 1)
|
|
209
|
+
elif channel_dim == 0:
|
|
210
|
+
pass
|
|
211
|
+
else:
|
|
212
|
+
raise ValueError('invalid channel_dim')
|
|
213
|
+
|
|
214
|
+
h, w = img.shape[1:]
|
|
215
|
+
pady1 = pady2 = padx1 = padx2 = 0
|
|
216
|
+
|
|
217
|
+
if h > w:
|
|
218
|
+
padx1 = (h - w) // 2
|
|
219
|
+
padx2 = h - w - padx1
|
|
220
|
+
elif w > h:
|
|
221
|
+
pady1 = (w - h) // 2
|
|
222
|
+
pady2 = w - h - pady1
|
|
223
|
+
|
|
224
|
+
img_padded = pad(img, padding=(padx1, pady1, padx2, pady2), padding_mode='constant')
|
|
225
|
+
|
|
226
|
+
if channel_dim == 2:
|
|
227
|
+
img_padded = img_padded.permute(1, 2, 0)
|
|
228
|
+
|
|
229
|
+
return img_padded
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
# qualitative
|
|
233
|
+
|
|
234
|
+
def split_sentence(inp, limit=9):
|
|
235
|
+
t_new, current_len = [], 0
|
|
236
|
+
for k, t in enumerate(inp.split(' ')):
|
|
237
|
+
current_len += len(t) + 1
|
|
238
|
+
t_new += [t+' ']
|
|
239
|
+
# not last
|
|
240
|
+
if current_len > limit and k != len(inp.split(' ')) - 1:
|
|
241
|
+
current_len = 0
|
|
242
|
+
t_new += ['\n']
|
|
243
|
+
|
|
244
|
+
t_new = ''.join(t_new)
|
|
245
|
+
return t_new
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
from matplotlib import pyplot as plt
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def plot(imgs, *preds, labels=None, scale=1, cmap=plt.cm.magma, aps=None, gt_labels=None, vmax=None):
|
|
252
|
+
|
|
253
|
+
row_off = 0 if labels is None else 1
|
|
254
|
+
_, ax = plt.subplots(len(imgs) + row_off, 1 + len(preds), figsize=(scale * float(1 + 2*len(preds)), scale * float(len(imgs)*2)))
|
|
255
|
+
[a.axis('off') for a in ax.flatten()]
|
|
256
|
+
|
|
257
|
+
if labels is not None:
|
|
258
|
+
for j in range(len(labels)):
|
|
259
|
+
t_new = split_sentence(labels[j], limit=6)
|
|
260
|
+
ax[0, 1+ j].text(0.5, 0.1, t_new, ha='center', fontsize=3+ 10*scale)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
for i in range(len(imgs)):
|
|
264
|
+
ax[i + row_off,0].imshow(imgs[i])
|
|
265
|
+
for j in range(len(preds)):
|
|
266
|
+
img = preds[j][i][0].detach().cpu().numpy()
|
|
267
|
+
|
|
268
|
+
if gt_labels is not None and labels[j] == gt_labels[i]:
|
|
269
|
+
print(j, labels[j], gt_labels[i])
|
|
270
|
+
edgecolor = 'red'
|
|
271
|
+
if aps is not None:
|
|
272
|
+
ax[i + row_off, 1 + j].text(30, 70, f'AP: {aps[i]:.3f}', color='red', fontsize=8)
|
|
273
|
+
else:
|
|
274
|
+
edgecolor = 'k'
|
|
275
|
+
|
|
276
|
+
rect = plt.Rectangle([0,0], img.shape[0], img.shape[1], facecolor="none",
|
|
277
|
+
edgecolor=edgecolor, linewidth=3)
|
|
278
|
+
ax[i + row_off,1 + j].add_patch(rect)
|
|
279
|
+
|
|
280
|
+
if vmax is None:
|
|
281
|
+
this_vmax = 1
|
|
282
|
+
elif vmax == 'per_prompt':
|
|
283
|
+
this_vmax = max([preds[j][_i][0].max() for _i in range(len(imgs))])
|
|
284
|
+
elif vmax == 'per_image':
|
|
285
|
+
this_vmax = max([preds[_j][i][0].max() for _j in range(len(preds))])
|
|
286
|
+
|
|
287
|
+
ax[i + row_off,1 + j].imshow(img, vmin=0, vmax=this_vmax, cmap=cmap)
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
# ax[i,1 + j].imshow(preds[j][i][0].detach().cpu().numpy(), vmin=preds[j].min(), vmax=preds[j].max())
|
|
291
|
+
plt.tight_layout()
|
|
292
|
+
plt.subplots_adjust(wspace=0.05, hspace=0.05)
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import inspect
|
|
3
|
+
import torch
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
import yaml
|
|
7
|
+
from shutil import copy, copytree
|
|
8
|
+
from os.path import join, dirname, realpath, expanduser, isfile, isdir, basename
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Logger(object):
|
|
12
|
+
|
|
13
|
+
def __getattr__(self, k):
|
|
14
|
+
return print
|
|
15
|
+
|
|
16
|
+
log = Logger()
|
|
17
|
+
|
|
18
|
+
def training_config_from_cli_args():
|
|
19
|
+
experiment_name = sys.argv[1]
|
|
20
|
+
experiment_id = int(sys.argv[2])
|
|
21
|
+
|
|
22
|
+
yaml_config = yaml.load(open(f'experiments/{experiment_name}'), Loader=yaml.SafeLoader)
|
|
23
|
+
|
|
24
|
+
config = yaml_config['configuration']
|
|
25
|
+
config = {**config, **yaml_config['individual_configurations'][experiment_id]}
|
|
26
|
+
config = AttributeDict(config)
|
|
27
|
+
return config
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def score_config_from_cli_args():
|
|
31
|
+
experiment_name = sys.argv[1]
|
|
32
|
+
experiment_id = int(sys.argv[2])
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
yaml_config = yaml.load(open(f'experiments/{experiment_name}'), Loader=yaml.SafeLoader)
|
|
36
|
+
|
|
37
|
+
config = yaml_config['test_configuration_common']
|
|
38
|
+
|
|
39
|
+
if type(yaml_config['test_configuration']) == list:
|
|
40
|
+
test_id = int(sys.argv[3])
|
|
41
|
+
config = {**config, **yaml_config['test_configuration'][test_id]}
|
|
42
|
+
else:
|
|
43
|
+
config = {**config, **yaml_config['test_configuration']}
|
|
44
|
+
|
|
45
|
+
if 'test_configuration' in yaml_config['individual_configurations'][experiment_id]:
|
|
46
|
+
config = {**config, **yaml_config['individual_configurations'][experiment_id]['test_configuration']}
|
|
47
|
+
|
|
48
|
+
train_checkpoint_id = yaml_config['individual_configurations'][experiment_id]['name']
|
|
49
|
+
|
|
50
|
+
config = AttributeDict(config)
|
|
51
|
+
return config, train_checkpoint_id
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def get_from_repository(local_name, repo_files, integrity_check=None, repo_dir='~/dataset_repository',
|
|
55
|
+
local_dir='~/datasets'):
|
|
56
|
+
""" copies files from repository to local folder.
|
|
57
|
+
|
|
58
|
+
repo_files: list of filenames or list of tuples [filename, target path]
|
|
59
|
+
|
|
60
|
+
e.g. get_from_repository('MyDataset', [['data/dataset1.tar', 'other/path/ds03.tar'])
|
|
61
|
+
will create a folder 'MyDataset' in local_dir, and extract the content of
|
|
62
|
+
'<repo_dir>/data/dataset1.tar' to <local_dir>/MyDataset/other/path.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
local_dir = realpath(join(expanduser(local_dir), local_name))
|
|
66
|
+
|
|
67
|
+
dataset_exists = True
|
|
68
|
+
|
|
69
|
+
# check if folder is available
|
|
70
|
+
if not isdir(local_dir):
|
|
71
|
+
dataset_exists = False
|
|
72
|
+
|
|
73
|
+
if integrity_check is not None:
|
|
74
|
+
try:
|
|
75
|
+
integrity_ok = integrity_check(local_dir)
|
|
76
|
+
except BaseException:
|
|
77
|
+
integrity_ok = False
|
|
78
|
+
|
|
79
|
+
if integrity_ok:
|
|
80
|
+
log.hint('Passed custom integrity check')
|
|
81
|
+
else:
|
|
82
|
+
log.hint('Custom integrity check failed')
|
|
83
|
+
|
|
84
|
+
dataset_exists = dataset_exists and integrity_ok
|
|
85
|
+
|
|
86
|
+
if not dataset_exists:
|
|
87
|
+
|
|
88
|
+
repo_dir = realpath(expanduser(repo_dir))
|
|
89
|
+
|
|
90
|
+
for i, filename in enumerate(repo_files):
|
|
91
|
+
|
|
92
|
+
if type(filename) == str:
|
|
93
|
+
origin, target = filename, filename
|
|
94
|
+
archive_target = join(local_dir, basename(origin))
|
|
95
|
+
extract_target = join(local_dir)
|
|
96
|
+
else:
|
|
97
|
+
origin, target = filename
|
|
98
|
+
archive_target = join(local_dir, dirname(target), basename(origin))
|
|
99
|
+
extract_target = join(local_dir, dirname(target))
|
|
100
|
+
|
|
101
|
+
archive_origin = join(repo_dir, origin)
|
|
102
|
+
|
|
103
|
+
log.hint(f'copy: {archive_origin} to {archive_target}')
|
|
104
|
+
|
|
105
|
+
# make sure the path exists
|
|
106
|
+
os.makedirs(dirname(archive_target), exist_ok=True)
|
|
107
|
+
|
|
108
|
+
if os.path.isfile(archive_target):
|
|
109
|
+
# only copy if size differs
|
|
110
|
+
if os.path.getsize(archive_target) != os.path.getsize(archive_origin):
|
|
111
|
+
log.hint(f'file exists but filesize differs: target {os.path.getsize(archive_target)} vs. origin {os.path.getsize(archive_origin)}')
|
|
112
|
+
copy(archive_origin, archive_target)
|
|
113
|
+
else:
|
|
114
|
+
copy(archive_origin, archive_target)
|
|
115
|
+
|
|
116
|
+
extract_archive(archive_target, extract_target, noarchive_ok=True)
|
|
117
|
+
|
|
118
|
+
# concurrent processes might have deleted the file
|
|
119
|
+
if os.path.isfile(archive_target):
|
|
120
|
+
os.remove(archive_target)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def extract_archive(filename, target_folder=None, noarchive_ok=False):
|
|
124
|
+
from subprocess import run, PIPE
|
|
125
|
+
|
|
126
|
+
if filename.endswith('.tgz') or filename.endswith('.tar'):
|
|
127
|
+
command = f'tar -xf {filename}'
|
|
128
|
+
command += f' -C {target_folder}' if target_folder is not None else ''
|
|
129
|
+
elif filename.endswith('.tar.gz'):
|
|
130
|
+
command = f'tar -xzf {filename}'
|
|
131
|
+
command += f' -C {target_folder}' if target_folder is not None else ''
|
|
132
|
+
elif filename.endswith('zip'):
|
|
133
|
+
command = f'unzip {filename}'
|
|
134
|
+
command += f' -d {target_folder}' if target_folder is not None else ''
|
|
135
|
+
else:
|
|
136
|
+
if noarchive_ok:
|
|
137
|
+
return
|
|
138
|
+
else:
|
|
139
|
+
raise ValueError(f'unsuppored file ending of {filename}')
|
|
140
|
+
|
|
141
|
+
log.hint(command)
|
|
142
|
+
result = run(command.split(), stdout=PIPE, stderr=PIPE)
|
|
143
|
+
if result.returncode != 0:
|
|
144
|
+
print(result.stdout, result.stderr)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class AttributeDict(dict):
|
|
148
|
+
"""
|
|
149
|
+
An extended dictionary that allows access to elements as atttributes and counts
|
|
150
|
+
these accesses. This way, we know if some attributes were never used.
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
def __init__(self, *args, **kwargs):
|
|
154
|
+
from collections import Counter
|
|
155
|
+
super().__init__(*args, **kwargs)
|
|
156
|
+
self.__dict__['counter'] = Counter()
|
|
157
|
+
|
|
158
|
+
def __getitem__(self, k):
|
|
159
|
+
self.__dict__['counter'][k] += 1
|
|
160
|
+
return super().__getitem__(k)
|
|
161
|
+
|
|
162
|
+
def __getattr__(self, k):
|
|
163
|
+
self.__dict__['counter'][k] += 1
|
|
164
|
+
return super().get(k)
|
|
165
|
+
|
|
166
|
+
def __setattr__(self, k, v):
|
|
167
|
+
return super().__setitem__(k, v)
|
|
168
|
+
|
|
169
|
+
def __delattr__(self, k, v):
|
|
170
|
+
return super().__delitem__(k, v)
|
|
171
|
+
|
|
172
|
+
def unused_keys(self, exceptions=()):
|
|
173
|
+
return [k for k in super().keys() if self.__dict__['counter'][k] == 0 and k not in exceptions]
|
|
174
|
+
|
|
175
|
+
def assume_no_unused_keys(self, exceptions=()):
|
|
176
|
+
if len(self.unused_keys(exceptions=exceptions)) > 0:
|
|
177
|
+
log.warning('Unused keys:', self.unused_keys(exceptions=exceptions))
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def get_attribute(name):
|
|
181
|
+
import importlib
|
|
182
|
+
|
|
183
|
+
if name is None:
|
|
184
|
+
raise ValueError('The provided attribute is None')
|
|
185
|
+
|
|
186
|
+
name_split = name.split('.')
|
|
187
|
+
mod = importlib.import_module('.'.join(name_split[:-1]))
|
|
188
|
+
return getattr(mod, name_split[-1])
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def filter_args(input_args, default_args):
|
|
193
|
+
|
|
194
|
+
updated_args = {k: input_args[k] if k in input_args else v for k, v in default_args.items()}
|
|
195
|
+
used_args = {k: v for k, v in input_args.items() if k in default_args}
|
|
196
|
+
unused_args = {k: v for k, v in input_args.items() if k not in default_args}
|
|
197
|
+
|
|
198
|
+
return AttributeDict(updated_args), AttributeDict(used_args), AttributeDict(unused_args)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def load_model(checkpoint_id, weights_file=None, strict=True, model_args='from_config', with_config=False):
|
|
202
|
+
|
|
203
|
+
config = json.load(open(join('logs', checkpoint_id, 'config.json')))
|
|
204
|
+
|
|
205
|
+
if model_args != 'from_config' and type(model_args) != dict:
|
|
206
|
+
raise ValueError('model_args must either be "from_config" or a dictionary of values')
|
|
207
|
+
|
|
208
|
+
model_cls = get_attribute(config['model'])
|
|
209
|
+
|
|
210
|
+
# load model
|
|
211
|
+
if model_args == 'from_config':
|
|
212
|
+
_, model_args, _ = filter_args(config, inspect.signature(model_cls).parameters)
|
|
213
|
+
|
|
214
|
+
model = model_cls(**model_args)
|
|
215
|
+
|
|
216
|
+
if weights_file is None:
|
|
217
|
+
weights_file = realpath(join('logs', checkpoint_id, 'weights.pth'))
|
|
218
|
+
else:
|
|
219
|
+
weights_file = realpath(join('logs', checkpoint_id, weights_file))
|
|
220
|
+
|
|
221
|
+
if isfile(weights_file):
|
|
222
|
+
weights = torch.load(weights_file)
|
|
223
|
+
for _, w in weights.items():
|
|
224
|
+
assert not torch.any(torch.isnan(w)), 'weights contain NaNs'
|
|
225
|
+
model.load_state_dict(weights, strict=strict)
|
|
226
|
+
else:
|
|
227
|
+
raise FileNotFoundError(f'model checkpoint {weights_file} was not found')
|
|
228
|
+
|
|
229
|
+
if with_config:
|
|
230
|
+
return model, config
|
|
231
|
+
|
|
232
|
+
return model
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
class TrainingLogger(object):
|
|
236
|
+
|
|
237
|
+
def __init__(self, model, log_dir, config=None, *args):
|
|
238
|
+
super().__init__()
|
|
239
|
+
self.model = model
|
|
240
|
+
self.base_path = join(f'logs/{log_dir}') if log_dir is not None else None
|
|
241
|
+
|
|
242
|
+
os.makedirs('logs/', exist_ok=True)
|
|
243
|
+
os.makedirs(self.base_path, exist_ok=True)
|
|
244
|
+
|
|
245
|
+
if config is not None:
|
|
246
|
+
json.dump(config, open(join(self.base_path, 'config.json'), 'w'))
|
|
247
|
+
|
|
248
|
+
def iter(self, i, **kwargs):
|
|
249
|
+
if i % 100 == 0 and 'loss' in kwargs:
|
|
250
|
+
loss = kwargs['loss']
|
|
251
|
+
print(f'iteration {i}: loss {loss:.4f}')
|
|
252
|
+
|
|
253
|
+
def save_weights(self, only_trainable=False, weight_file='weights.pth'):
|
|
254
|
+
if self.model is None:
|
|
255
|
+
raise AttributeError('You need to provide a model reference when initializing TrainingTracker to save weights.')
|
|
256
|
+
|
|
257
|
+
weights_path = join(self.base_path, weight_file)
|
|
258
|
+
|
|
259
|
+
weight_dict = self.model.state_dict()
|
|
260
|
+
|
|
261
|
+
if only_trainable:
|
|
262
|
+
weight_dict = {n: weight_dict[n] for n, p in self.model.named_parameters() if p.requires_grad}
|
|
263
|
+
|
|
264
|
+
torch.save(weight_dict, weights_path)
|
|
265
|
+
log.info(f'Saved weights to {weights_path}')
|
|
266
|
+
|
|
267
|
+
def __enter__(self):
|
|
268
|
+
return self
|
|
269
|
+
|
|
270
|
+
def __exit__(self, type, value, traceback):
|
|
271
|
+
""" automatically stop processes if used in a context manager """
|
|
272
|
+
pass
|