segmodels-keras 0.1.0.dev11__tar.gz → 0.1.0.dev13__tar.gz

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 (45) hide show
  1. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/PKG-INFO +1 -1
  2. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/pyproject.toml +1 -0
  3. segmodels_keras-0.1.0.dev13/segmodels_keras/_version.py +2 -0
  4. segmodels_keras-0.1.0.dev13/segmodels_keras/backbones/backbones_factory.py +423 -0
  5. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras.egg-info/PKG-INFO +1 -1
  6. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/tests/test_models.py +49 -38
  7. segmodels_keras-0.1.0.dev11/segmodels_keras/_version.py +0 -2
  8. segmodels_keras-0.1.0.dev11/segmodels_keras/backbones/backbones_factory.py +0 -326
  9. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/LICENSE +0 -0
  10. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/MANIFEST.in +0 -0
  11. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/README.rst +0 -0
  12. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras/__init__.py +0 -0
  13. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras/_compat.py +0 -0
  14. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras/backbones/__init__.py +0 -0
  15. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras/backbones/_common_blocks.py +0 -0
  16. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras/backbones/_weights.py +0 -0
  17. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras/backbones/inception_resnet_v2.py +0 -0
  18. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras/backbones/inception_v3.py +0 -0
  19. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras/backbones/resnet_18_34.py +0 -0
  20. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras/backbones/resnet_common.py +0 -0
  21. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras/base/__init__.py +0 -0
  22. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras/base/functional.py +0 -0
  23. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras/base/objects.py +0 -0
  24. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras/losses.py +0 -0
  25. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras/metrics.py +0 -0
  26. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras/models/__init__.py +0 -0
  27. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras/models/_common_blocks.py +0 -0
  28. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras/models/_utils.py +0 -0
  29. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras/models/fpn.py +0 -0
  30. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras/models/linknet.py +0 -0
  31. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras/models/model_factory.py +0 -0
  32. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras/models/pspnet.py +0 -0
  33. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras/models/unet.py +0 -0
  34. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras/py.typed +0 -0
  35. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras/utils.py +0 -0
  36. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras.egg-info/SOURCES.txt +0 -0
  37. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras.egg-info/dependency_links.txt +0 -0
  38. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras.egg-info/requires.txt +0 -0
  39. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/segmodels_keras.egg-info/top_level.txt +0 -0
  40. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/setup.cfg +0 -0
  41. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/setup.py +0 -0
  42. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/tests/test_backbones.py +0 -0
  43. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/tests/test_helper.py +0 -0
  44. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/tests/test_metrics.py +0 -0
  45. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev13}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: segmodels_keras
3
- Version: 0.1.0.dev11
3
+ Version: 0.1.0.dev13
4
4
  Summary: Image segmentation models with pre-trained backbones with Keras.
5
5
  Home-page: https://github.com/orthoseg/segmodels_keras
6
6
  Author: Pieter Roggemans
@@ -51,6 +51,7 @@ ignore = [
51
51
  ### Intentionally disabled
52
52
  "E402", # Module level import not at top of file
53
53
  "PLR0913", # Too many arguments to function call
54
+ "PLR0917", # Too many positional arguments
54
55
  "PLR0911", # Too many returns
55
56
  "PLR0912", # Too many branches
56
57
  "PLR0915", # Too many statements
@@ -0,0 +1,2 @@
1
+ __versioninfo__: tuple[int, int, int, str] = (0, 1, 0, "dev13")
2
+ __version__: str = ".".join(map(str, __versioninfo__))
@@ -0,0 +1,423 @@
1
+ """Backbones factory module."""
2
+
3
+ import keras
4
+ import keras.applications as ka
5
+
6
+ from . import inception_resnet_v2 as irv2
7
+ from . import inception_v3 as iv3
8
+ from . import resnet_18_34
9
+
10
+
11
+ class BackbonesFactory:
12
+ """Dict with all supported backbones.
13
+
14
+ Each backbone is represented as a tuple of 3 elements:
15
+ 1. Backbone class
16
+ 2. Preprocessing function
17
+ 3. List of layers to take features from backbone in the following order:
18
+ (x16, x8, x4, x2, x1) - `x4` mean that features has 4 times less spatial
19
+ resolution (Height x Width) than input image.
20
+ """
21
+
22
+ _models = { # noqa: RUF012
23
+ # Feature sizes in comments are HxWxC for a 512x512 input.
24
+ # ResNets < 50 layers are NOT available via keras.applications, so use a
25
+ # torchvision-compatible post-activation BasicBlock implementation.
26
+ # Layer names follow the keras.applications ResNet50+ naming convention.
27
+ "resnet18": (
28
+ resnet_18_34.ResNet18,
29
+ resnet_18_34.preprocess_input,
30
+ # resnet18: layer1=2, layer2=2, layer3=2, layer4=2 blocks
31
+ # backbone output = conv5_block2_out (512ch); skips are the 4 stages before
32
+ (
33
+ "conv4_block2_out", # 32x32x256
34
+ "conv3_block2_out", # 64x64x128
35
+ "conv2_block2_out", # 128x128x64
36
+ "conv1_relu", # 256x256x64
37
+ ),
38
+ ),
39
+ "resnet34": (
40
+ resnet_18_34.ResNet34,
41
+ resnet_18_34.preprocess_input,
42
+ # resnet34: layer1=3, layer2=4, layer3=6, layer4=3 blocks
43
+ # backbone output = conv5_block3_out (512ch); skips are the 4 stages before
44
+ (
45
+ "conv4_block6_out", # 32x32x256
46
+ "conv3_block4_out", # 64x64x128
47
+ "conv2_block3_out", # 128x128x64
48
+ "conv1_relu", # 256x256x64
49
+ ),
50
+ ),
51
+ # ResNets > 50 layers are available via keras.applications, so use those.
52
+ # Skip layers (inverted) from https://github.com/yingkaisha/keras-unet-collection
53
+ "resnet50": (
54
+ ka.ResNet50,
55
+ ka.resnet.preprocess_input,
56
+ (
57
+ "conv4_block6_out", # 32x32x1024
58
+ "conv3_block4_out", # 64x64x512
59
+ "conv2_block3_out", # 128x128x256
60
+ "conv1_relu", # 256x256x64
61
+ ),
62
+ ),
63
+ "resnet101": (
64
+ ka.ResNet101,
65
+ ka.resnet.preprocess_input,
66
+ (
67
+ "conv4_block23_out", # 32x32x1024
68
+ "conv3_block4_out", # 64x64x512
69
+ "conv2_block3_out", # 128x128x256
70
+ "conv1_relu", # 256x256x64
71
+ ),
72
+ ),
73
+ "resnet152": (
74
+ ka.ResNet152,
75
+ ka.resnet.preprocess_input,
76
+ (
77
+ "conv4_block36_out", # 32x32x1024
78
+ "conv3_block8_out", # 64x64x512
79
+ "conv2_block3_out", # 128x128x256
80
+ "conv1_relu", # 256x256x64
81
+ ),
82
+ ),
83
+ # ResNetV2
84
+ # Skip layers (inverted) from https://github.com/yingkaisha/keras-unet-collection
85
+ "resnet50v2": (
86
+ ka.ResNet50V2,
87
+ ka.resnet_v2.preprocess_input,
88
+ (
89
+ "conv4_block6_1_relu", # 32x32x256
90
+ "conv3_block4_1_relu", # 64x64x128
91
+ "conv2_block3_1_relu", # 128x128x64
92
+ "conv1_conv", # 256x256x64
93
+ ),
94
+ ),
95
+ "resnet101v2": (
96
+ ka.ResNet101V2,
97
+ ka.resnet_v2.preprocess_input,
98
+ (
99
+ "conv4_block23_1_relu", # 32x32x256
100
+ "conv3_block4_1_relu", # 64x64x128
101
+ "conv2_block3_1_relu", # 128x128x64
102
+ "conv1_conv", # 256x256x64
103
+ ),
104
+ ),
105
+ "resnet152v2": (
106
+ ka.ResNet152V2,
107
+ ka.resnet_v2.preprocess_input,
108
+ (
109
+ "conv4_block36_1_relu", # 32x32x256
110
+ "conv3_block8_1_relu", # 64x64x128
111
+ "conv2_block3_1_relu", # 128x128x64
112
+ "conv1_conv", # 256x256x64
113
+ ),
114
+ ),
115
+ # VGG
116
+ # Skip layers from segmentation_models
117
+ "vgg16": (
118
+ ka.vgg16.VGG16,
119
+ ka.vgg16.preprocess_input,
120
+ (
121
+ "block5_conv3", # 32x32x512
122
+ "block4_conv3", # 64x64x512
123
+ "block3_conv3", # 128x128x256
124
+ "block2_conv2", # 256x256x128
125
+ "block1_conv2", # 512x512x64
126
+ ),
127
+ ),
128
+ "vgg19": (
129
+ ka.vgg19.VGG19,
130
+ ka.vgg19.preprocess_input,
131
+ (
132
+ "block5_conv4", # 32x32x512
133
+ "block4_conv4", # 64x64x512
134
+ "block3_conv4", # 128x128x256
135
+ "block2_conv2", # 256x256x128
136
+ "block1_conv2", # 512x512x64
137
+ ),
138
+ ),
139
+ # DenseNet
140
+ # Skip layers (inverted) from https://github.com/yingkaisha/keras-unet-collection
141
+ "densenet121": (
142
+ ka.densenet.DenseNet121,
143
+ ka.densenet.preprocess_input,
144
+ (
145
+ "pool4_conv", # 32x32x512
146
+ "pool3_conv", # 64x64x256
147
+ "pool2_conv", # 128x128x128
148
+ "conv1/relu", # 256x256x64
149
+ ),
150
+ ),
151
+ "densenet169": (
152
+ ka.densenet.DenseNet169,
153
+ ka.densenet.preprocess_input,
154
+ (
155
+ "pool4_conv", # 32x32x640
156
+ "pool3_conv", # 64x64x256
157
+ "pool2_conv", # 128x128x128
158
+ "conv1/relu", # 256x256x64
159
+ ),
160
+ ),
161
+ "densenet201": (
162
+ ka.densenet.DenseNet201,
163
+ ka.densenet.preprocess_input,
164
+ (
165
+ "pool4_conv", # 32x32x896
166
+ "pool3_conv", # 64x64x256
167
+ "pool2_conv", # 128x128x128
168
+ "conv1/relu", # 256x256x64
169
+ ),
170
+ ),
171
+ # Inception
172
+ # Skip layers from segmentation_models
173
+ "inceptionresnetv2": (
174
+ irv2.InceptionResNetV2,
175
+ irv2.preprocess_input,
176
+ # Use the layer indexes instead of names because otherwise loading weights
177
+ # saved with keras 2 cannot be loaded with keras 3.
178
+ # (
179
+ # "activation_161",
180
+ # "activation_74",
181
+ # "activation_3",
182
+ # "activation",
183
+ # # "input_1",
184
+ # ),
185
+ (
186
+ 594, # 32x32x1088
187
+ 260, # 64x64x320
188
+ 16, # 128x128x192
189
+ 9, # 256x256x64
190
+ ),
191
+ ),
192
+ "inceptionv3": (
193
+ iv3.InceptionV3,
194
+ iv3.preprocess_input,
195
+ (
196
+ 228, # 32x32x768
197
+ 86, # 64x64x288
198
+ 16, # 128x128x192
199
+ 9, # 256x256x64
200
+ ),
201
+ ),
202
+ # MobileNet
203
+ # Skip layers from segmentation_models
204
+ "mobilenet": (
205
+ ka.mobilenet.MobileNet,
206
+ ka.mobilenet.preprocess_input,
207
+ (
208
+ "conv_pw_11_relu", # 32x32x512
209
+ "conv_pw_5_relu", # 64x64x256
210
+ "conv_pw_3_relu", # 128x128x128
211
+ "conv_pw_1_relu", # 256x256x64
212
+ ),
213
+ ),
214
+ "mobilenetv2": (
215
+ ka.mobilenet_v2.MobileNetV2,
216
+ ka.mobilenet_v2.preprocess_input,
217
+ (
218
+ "block_13_expand_relu", # 32x32x576
219
+ "block_6_expand_relu", # 64x64x192
220
+ "block_3_expand_relu", # 128x128x144
221
+ "block_1_expand_relu", # 256x256x96
222
+ ),
223
+ ),
224
+ # EfficientNet
225
+ # Skip layers from segmentation_models
226
+ "efficientnetb0": [
227
+ ka.EfficientNetB0,
228
+ ka.efficientnet.preprocess_input,
229
+ (
230
+ "block6a_expand_activation", # 32x32x672
231
+ "block4a_expand_activation", # 64x64x240
232
+ "block3a_expand_activation", # 128x128x144
233
+ "block2a_expand_activation", # 256x256x96
234
+ ),
235
+ ],
236
+ "efficientnetb1": [
237
+ ka.EfficientNetB1,
238
+ ka.efficientnet.preprocess_input,
239
+ (
240
+ "block6a_expand_activation", # 32x32x672
241
+ "block4a_expand_activation", # 64x64x240
242
+ "block3a_expand_activation", # 128x128x144
243
+ "block2a_expand_activation", # 256x256x96
244
+ ),
245
+ ],
246
+ "efficientnetb2": [
247
+ ka.EfficientNetB2,
248
+ ka.efficientnet.preprocess_input,
249
+ (
250
+ "block6a_expand_activation", # 32x32x720
251
+ "block4a_expand_activation", # 64x64x288
252
+ "block3a_expand_activation", # 128x128x144
253
+ "block2a_expand_activation", # 256x256x96
254
+ ),
255
+ ],
256
+ "efficientnetb3": [
257
+ ka.EfficientNetB3,
258
+ ka.efficientnet.preprocess_input,
259
+ (
260
+ "block6a_expand_activation", # 32x32x816
261
+ "block4a_expand_activation", # 64x64x288
262
+ "block3a_expand_activation", # 128x128x192
263
+ "block2a_expand_activation", # 256x256x144
264
+ ),
265
+ ],
266
+ "efficientnetb4": [
267
+ ka.EfficientNetB4,
268
+ ka.efficientnet.preprocess_input,
269
+ (
270
+ "block6a_expand_activation", # 32x32x960
271
+ "block4a_expand_activation", # 64x64x336
272
+ "block3a_expand_activation", # 128x128x192
273
+ "block2a_expand_activation", # 256x256x144
274
+ ),
275
+ ],
276
+ "efficientnetb5": [
277
+ ka.EfficientNetB5,
278
+ ka.efficientnet.preprocess_input,
279
+ (
280
+ "block6a_expand_activation", # 32x32x1056
281
+ "block4a_expand_activation", # 64x64x384
282
+ "block3a_expand_activation", # 128x128x240
283
+ "block2a_expand_activation", # 256x256x144
284
+ ),
285
+ ],
286
+ "efficientnetb6": [
287
+ ka.EfficientNetB6,
288
+ ka.efficientnet.preprocess_input,
289
+ (
290
+ "block6a_expand_activation", # 32x32x1200
291
+ "block4a_expand_activation", # 64x64x432
292
+ "block3a_expand_activation", # 128x128x240
293
+ "block2a_expand_activation", # 256x256x192
294
+ ),
295
+ ],
296
+ "efficientnetb7": [
297
+ ka.EfficientNetB7,
298
+ ka.efficientnet.preprocess_input,
299
+ (
300
+ "block6a_expand_activation", # 32x32x1344
301
+ "block4a_expand_activation", # 64x64x480
302
+ "block3a_expand_activation", # 128x128x288
303
+ "block2a_expand_activation", # 256x256x192
304
+ ),
305
+ ],
306
+ # EfficientNetV2
307
+ # Stage layout differs from EfficientNet-Bx.
308
+ # Skip layers selected to match decoder resolutions:
309
+ # x16 -> x8 -> x4 -> x2
310
+ #
311
+ # Tests with more compact skip connection layers ("block5i_add" (32x32x160),
312
+ # "block3d_add" (64x64x64), "block2d_add" (128x128x48), "stem_activation"
313
+ # (256x256x24)) showed that the decrease in model size was small (30 MB) but
314
+ # the accuracy performance impact was significant (-1.5%).
315
+ "efficientnetv2b0": (
316
+ ka.EfficientNetV2B0,
317
+ ka.efficientnet_v2.preprocess_input,
318
+ (
319
+ "block4b_expand_conv", # 32x32x384
320
+ "block3a_expand_conv", # 64x64x128
321
+ "block2a_expand_activation", # 128x128x64
322
+ "stem_activation", # 256x256x32
323
+ ),
324
+ ),
325
+ "efficientnetv2b1": (
326
+ ka.EfficientNetV2B1,
327
+ ka.efficientnet_v2.preprocess_input,
328
+ (
329
+ "block4b_expand_conv", # 32x32x384
330
+ "block3a_expand_conv", # 64x64x128
331
+ "block2a_expand_activation", # 128x128x64
332
+ "stem_activation", # 256x256x32
333
+ ),
334
+ ),
335
+ "efficientnetv2b2": (
336
+ ka.EfficientNetV2B2,
337
+ ka.efficientnet_v2.preprocess_input,
338
+ (
339
+ "block4b_expand_conv", # 32x32x416
340
+ "block3a_expand_conv", # 64x64x128
341
+ "block2a_expand_activation", # 128x128x64
342
+ "stem_activation", # 256x256x32
343
+ ),
344
+ ),
345
+ "efficientnetv2b3": (
346
+ ka.EfficientNetV2B3,
347
+ ka.efficientnet_v2.preprocess_input,
348
+ (
349
+ "block4b_expand_conv", # 32x32x448
350
+ "block3a_expand_conv", # 64x64x160
351
+ "block2a_expand_activation", # 128x128x64
352
+ "stem_activation", # 256x256x40
353
+ ),
354
+ ),
355
+ "efficientnetv2s": (
356
+ ka.EfficientNetV2S,
357
+ ka.efficientnet_v2.preprocess_input,
358
+ (
359
+ "block6a_expand_conv", # 32x32x960
360
+ "block4a_expand_conv", # 64x64x256
361
+ "block2a_expand_activation", # 128x128x96
362
+ "stem_activation", # 256x256x24
363
+ ),
364
+ ),
365
+ "efficientnetv2m": (
366
+ ka.EfficientNetV2M,
367
+ ka.efficientnet_v2.preprocess_input,
368
+ (
369
+ "block6a_expand_conv", # 32x32x1056
370
+ "block4a_expand_conv", # 64x64x320
371
+ "block2a_expand_activation", # 128x128x96
372
+ "stem_activation", # 256x256x24
373
+ ),
374
+ ),
375
+ "efficientnetv2l": (
376
+ ka.EfficientNetV2L,
377
+ ka.efficientnet_v2.preprocess_input,
378
+ (
379
+ "block6a_expand_conv", # 32x32x1344
380
+ "block4a_expand_conv", # 64x64x384
381
+ "block2a_expand_activation", # 128x128x128
382
+ "stem_activation", # 256x256x32
383
+ ),
384
+ ),
385
+ }
386
+
387
+ @property
388
+ def models(self):
389
+ """Get the dictionary of available backbones."""
390
+ return self._models
391
+
392
+ def models_names(self):
393
+ """Get the list of available backbone names."""
394
+ return list(self.models.keys())
395
+
396
+ """Get a backbone model by name."""
397
+
398
+ def get_backbone(self, name, *args, **kwargs) -> keras.Model:
399
+ """Get a backbone model by name."""
400
+ backbone = self._models.get(name)
401
+ if backbone is None:
402
+ raise ValueError(f"Backbone with name '{name}' is not supported.")
403
+
404
+ model_fn, _, _ = backbone
405
+ model = model_fn(*args, **kwargs)
406
+ return model
407
+
408
+ def get_feature_layers(self, name, n=5):
409
+ """Get the list of skip layers for a backbone by name."""
410
+ return self._models.get(name)[2][:n]
411
+
412
+ def get_preprocessing(self, name):
413
+ """Get the preprocessing function for a backbone by name."""
414
+ return self._models.get(name)[1]
415
+
416
+ def get_custom_objects(self, name):
417
+ """Get the custom objects for a backbone by name."""
418
+ if name == "inceptionresnetv2":
419
+ return irv2.get_custom_objects()
420
+ return {}
421
+
422
+
423
+ Backbones = BackbonesFactory()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: segmodels_keras
3
- Version: 0.1.0.dev11
3
+ Version: 0.1.0.dev13
4
4
  Summary: Image segmentation models with pre-trained backbones with Keras.
5
5
  Home-page: https://github.com/orthoseg/segmodels_keras
6
6
  Author: Pieter Roggemans
@@ -22,25 +22,25 @@ def str2bool(v):
22
22
  return v.lower() in ("yes", "true", "t", "1")
23
23
 
24
24
 
25
- def get_test_backbones():
26
- is_full = str2bool(os.environ.get("FULL_TEST", "False"))
25
+ def get_test_backbones(with_weights: bool):
26
+ if with_weights:
27
+ is_full = str2bool(os.environ.get("FULL_TEST", "False"))
28
+ else:
29
+ is_full = str2bool(os.environ.get("FULL_TEST_WEIGHTS", "False"))
30
+
27
31
  if not is_full:
28
- return [
29
- "resnet34",
30
- "resnet50",
31
- "inceptionresnetv2",
32
- "efficientnetb0",
33
- "efficientnetv2s",
34
- ]
32
+ return ["resnet34", "resnet50", "inceptionresnetv2", "efficientnetb0"]
35
33
  else:
36
34
  return get_available_backbone_names()
37
35
 
38
36
 
39
37
  def keras_test(func):
40
38
  """Function wrapper to clean up after TensorFlow tests.
41
- # Arguments
39
+
40
+ Args:
42
41
  func: test function to clean up after.
43
- # Returns
42
+
43
+ Returns:
44
44
  A function wrapping the input function.
45
45
  """
46
46
 
@@ -53,40 +53,37 @@ def keras_test(func):
53
53
  return wrapper
54
54
 
55
55
 
56
- @pytest.mark.parametrize("backbone_name", get_test_backbones())
56
+ @pytest.mark.parametrize("backbone_name", get_test_backbones(with_weights=False))
57
57
  @pytest.mark.parametrize(
58
- "model_name, input_shape, encoder_weights",
58
+ "model_name, input_shape",
59
59
  [
60
- ("unet", None, None),
61
- ("unet", None, "imagenet"),
62
- ("unet", (256, 256, 4), None),
63
- ("linknet", None, None),
64
- ("linknet", (256, 256, 4), None),
65
- ("pspnet", (384, 384, 4), None),
66
- ("fpn", None, None),
67
- ("fpn", (256, 256, 4), None),
60
+ ("unet", None),
61
+ ("unet", (256, 256, 4)),
62
+ ("linknet", None),
63
+ ("linknet", (256, 256, 4)),
64
+ ("pspnet", (384, 384, 4)),
65
+ ("fpn", None),
66
+ ("fpn", (256, 256, 4)),
68
67
  ],
69
68
  )
70
69
  @keras_test
71
- def test_get_model(model_name, backbone_name, input_shape, encoder_weights):
72
- """Test all segmentation models with different backbones.
70
+ def test_get_model(model_name, backbone_name, input_shape):
71
+ """Test creation of model/backbone combinations.
73
72
 
74
- input_shape=None means any shape (32x32 used in test), otherwise a fixed shape is
75
- used.
73
+ No weights are used in this test to avoid downloading pretrained models.
76
74
  """
77
- n_channels = 3 if input_shape is None else input_shape[-1]
78
- test_shape = (1, 32, 32, n_channels) if input_shape is None else (1, *input_shape)
75
+ _assert_get_model_output_shape(model_name, backbone_name, input_shape, None)
79
76
 
80
- x = np.ones(test_shape)
81
- model = get_model(
82
- model_name,
83
- backbone_name=backbone_name,
84
- input_shape=input_shape or (None, None, n_channels),
85
- encoder_weights=encoder_weights,
86
- )
87
- y = model.predict(x)
88
77
 
89
- assert x.shape[:-1] == y.shape[:-1]
78
+ @pytest.mark.parametrize("backbone_name", get_test_backbones(with_weights=True))
79
+ @keras_test
80
+ def test_get_model_with_imagenet_weights(backbone_name):
81
+ """Test model creation with ImageNet-pretrained encoder weights.
82
+
83
+ Only tested on a limited set of backbones as this will download the pretrained
84
+ weights.
85
+ """
86
+ _assert_get_model_output_shape("unet", backbone_name, None, "imagenet")
90
87
 
91
88
 
92
89
  def test_get_model_invalid_name():
@@ -113,5 +110,19 @@ def test_get_model_weights_notop_keras3():
113
110
  assert len(model.layers) > 0
114
111
 
115
112
 
116
- if __name__ == "__main__":
117
- pytest.main([__file__])
113
+ def _assert_get_model_output_shape(
114
+ model_name, backbone_name, input_shape, encoder_weights
115
+ ):
116
+ n_channels = 3 if input_shape is None else input_shape[-1]
117
+ test_shape = (1, 32, 32, n_channels) if input_shape is None else (1, *input_shape)
118
+
119
+ x = np.ones(test_shape)
120
+ model = get_model(
121
+ model_name,
122
+ backbone_name=backbone_name,
123
+ input_shape=input_shape or (None, None, n_channels),
124
+ encoder_weights=encoder_weights,
125
+ )
126
+ y = model.predict(x)
127
+
128
+ assert x.shape[:-1] == y.shape[:-1]
@@ -1,2 +0,0 @@
1
- __versioninfo__: tuple[int, int, int, str] = (0, 1, 0, "dev11")
2
- __version__: str = ".".join(map(str, __versioninfo__))
@@ -1,326 +0,0 @@
1
- """Backbones factory module."""
2
-
3
- import keras
4
- import keras.applications as ka
5
-
6
- from . import inception_resnet_v2 as irv2
7
- from . import inception_v3 as iv3
8
- from . import resnet_18_34
9
-
10
-
11
- class BackbonesFactory:
12
- """Dict with all supported backbones.
13
-
14
- Each backbone is represented as a tuple of 3 elements:
15
- 1. Backbone class
16
- 2. Preprocessing function
17
- 3. List of layers to take features from backbone in the following order:
18
- (x16, x8, x4, x2, x1) - `x4` mean that features has 4 times less spatial
19
- resolution (Height x Width) than input image.
20
- """
21
-
22
- _models = { # noqa: RUF012
23
- # ResNets < 50 layers are NOT available via keras.applications, so use a
24
- # torchvision-compatible post-activation BasicBlock implementation.
25
- # Layer names follow the keras.applications ResNet50+ naming convention.
26
- "resnet18": (
27
- resnet_18_34.ResNet18,
28
- resnet_18_34.preprocess_input,
29
- # resnet18: layer1=2, layer2=2, layer3=2, layer4=2 blocks
30
- # backbone output = conv5_block2_out (512ch); skips are the 4 stages before
31
- ("conv4_block2_out", "conv3_block2_out", "conv2_block2_out", "conv1_relu"),
32
- ),
33
- "resnet34": (
34
- resnet_18_34.ResNet34,
35
- resnet_18_34.preprocess_input,
36
- # resnet34: layer1=3, layer2=4, layer3=6, layer4=3 blocks
37
- # backbone output = conv5_block3_out (512ch); skips are the 4 stages before
38
- ("conv4_block6_out", "conv3_block4_out", "conv2_block3_out", "conv1_relu"),
39
- ),
40
- # ResNets > 50 layers are available via keras.applications, so use those.
41
- # Skip layers (inverted) from https://github.com/yingkaisha/keras-unet-collection
42
- "resnet50": (
43
- ka.ResNet50,
44
- ka.resnet.preprocess_input,
45
- ("conv4_block6_out", "conv3_block4_out", "conv2_block3_out", "conv1_relu"),
46
- ),
47
- "resnet101": (
48
- ka.ResNet101,
49
- ka.resnet.preprocess_input,
50
- ("conv4_block23_out", "conv3_block4_out", "conv2_block3_out", "conv1_relu"),
51
- ),
52
- "resnet152": (
53
- ka.ResNet152,
54
- ka.resnet.preprocess_input,
55
- ("conv4_block36_out", "conv3_block8_out", "conv2_block3_out", "conv1_relu"),
56
- ),
57
- # ResNetV2
58
- # Skip layers (inverted) from https://github.com/yingkaisha/keras-unet-collection
59
- "resnet50v2": (
60
- ka.ResNet50V2,
61
- ka.resnet_v2.preprocess_input,
62
- (
63
- "conv4_block6_1_relu",
64
- "conv3_block4_1_relu",
65
- "conv2_block3_1_relu",
66
- "conv1_conv",
67
- ),
68
- ),
69
- "resnet101v2": (
70
- ka.ResNet101V2,
71
- ka.resnet_v2.preprocess_input,
72
- (
73
- "conv4_block23_1_relu",
74
- "conv3_block4_1_relu",
75
- "conv2_block3_1_relu",
76
- "conv1_conv",
77
- ),
78
- ),
79
- "resnet152v2": (
80
- ka.ResNet152V2,
81
- ka.resnet_v2.preprocess_input,
82
- (
83
- "conv4_block36_1_relu",
84
- "conv3_block8_1_relu",
85
- "conv2_block3_1_relu",
86
- "conv1_conv",
87
- ),
88
- ),
89
- # VGG
90
- # Skip layers from segmentation_models
91
- "vgg16": (
92
- ka.vgg16.VGG16,
93
- ka.vgg16.preprocess_input,
94
- (
95
- "block5_conv3",
96
- "block4_conv3",
97
- "block3_conv3",
98
- "block2_conv2",
99
- "block1_conv2",
100
- ),
101
- ),
102
- "vgg19": (
103
- ka.vgg19.VGG19,
104
- ka.vgg19.preprocess_input,
105
- (
106
- "block5_conv4",
107
- "block4_conv4",
108
- "block3_conv4",
109
- "block2_conv2",
110
- "block1_conv2",
111
- ),
112
- ),
113
- # DenseNet
114
- # Skip layers (inverted) from https://github.com/yingkaisha/keras-unet-collection
115
- "densenet121": (
116
- ka.densenet.DenseNet121,
117
- ka.densenet.preprocess_input,
118
- # (311, 139, 51, 4),
119
- ("pool4_conv", "pool3_conv", "pool2_conv", "conv1/relu"),
120
- ),
121
- "densenet169": (
122
- ka.densenet.DenseNet169,
123
- ka.densenet.preprocess_input,
124
- # (367, 139, 51, 4),
125
- ("pool4_conv", "pool3_conv", "pool2_conv", "conv1/relu"),
126
- ),
127
- "densenet201": (
128
- ka.densenet.DenseNet201,
129
- ka.densenet.preprocess_input,
130
- # (479, 139, 51, 4),
131
- ("pool4_conv", "pool3_conv", "pool2_conv", "conv1/relu"),
132
- ),
133
- # Inception
134
- # Skip layers from segmentation_models
135
- "inceptionresnetv2": (
136
- irv2.InceptionResNetV2,
137
- irv2.preprocess_input,
138
- # Use the layer indexes instead of names because otherwise loading weights
139
- # saved with keras 2 cannot be loaded with keras 3.
140
- # (
141
- # "activation_161",
142
- # "activation_74",
143
- # "activation_3",
144
- # "activation",
145
- # # "input_1",
146
- # ),
147
- (594, 260, 16, 9),
148
- ),
149
- "inceptionv3": (iv3.InceptionV3, iv3.preprocess_input, (228, 86, 16, 9)),
150
- # MobileNet
151
- # Skip layers from segmentation_models
152
- "mobilenet": (
153
- ka.mobilenet.MobileNet,
154
- ka.mobilenet.preprocess_input,
155
- (
156
- "conv_pw_11_relu",
157
- "conv_pw_5_relu",
158
- "conv_pw_3_relu",
159
- "conv_pw_1_relu",
160
- ),
161
- ),
162
- "mobilenetv2": (
163
- ka.mobilenet_v2.MobileNetV2,
164
- ka.mobilenet_v2.preprocess_input,
165
- (
166
- "block_13_expand_relu",
167
- "block_6_expand_relu",
168
- "block_3_expand_relu",
169
- "block_1_expand_relu",
170
- ),
171
- ),
172
- # EfficientNet
173
- # Skip layers from segmentation_models
174
- "efficientnetb0": [
175
- ka.EfficientNetB0,
176
- ka.efficientnet.preprocess_input,
177
- (
178
- "block6a_expand_activation",
179
- "block4a_expand_activation",
180
- "block3a_expand_activation",
181
- "block2a_expand_activation",
182
- ),
183
- ],
184
- "efficientnetb1": [
185
- ka.EfficientNetB1,
186
- ka.efficientnet.preprocess_input,
187
- (
188
- "block6a_expand_activation",
189
- "block4a_expand_activation",
190
- "block3a_expand_activation",
191
- "block2a_expand_activation",
192
- ),
193
- ],
194
- "efficientnetb2": [
195
- ka.EfficientNetB2,
196
- ka.efficientnet.preprocess_input,
197
- (
198
- "block6a_expand_activation",
199
- "block4a_expand_activation",
200
- "block3a_expand_activation",
201
- "block2a_expand_activation",
202
- ),
203
- ],
204
- "efficientnetb3": [
205
- ka.EfficientNetB3,
206
- ka.efficientnet.preprocess_input,
207
- (
208
- "block6a_expand_activation",
209
- "block4a_expand_activation",
210
- "block3a_expand_activation",
211
- "block2a_expand_activation",
212
- ),
213
- ],
214
- "efficientnetb4": [
215
- ka.EfficientNetB4,
216
- ka.efficientnet.preprocess_input,
217
- (
218
- "block6a_expand_activation",
219
- "block4a_expand_activation",
220
- "block3a_expand_activation",
221
- "block2a_expand_activation",
222
- ),
223
- ],
224
- "efficientnetb5": [
225
- ka.EfficientNetB5,
226
- ka.efficientnet.preprocess_input,
227
- (
228
- "block6a_expand_activation",
229
- "block4a_expand_activation",
230
- "block3a_expand_activation",
231
- "block2a_expand_activation",
232
- ),
233
- ],
234
- "efficientnetb6": [
235
- ka.EfficientNetB6,
236
- ka.efficientnet.preprocess_input,
237
- (
238
- "block6a_expand_activation",
239
- "block4a_expand_activation",
240
- "block3a_expand_activation",
241
- "block2a_expand_activation",
242
- ),
243
- ],
244
- "efficientnetb7": [
245
- ka.EfficientNetB7,
246
- ka.efficientnet.preprocess_input,
247
- (
248
- "block6a_expand_activation",
249
- "block4a_expand_activation",
250
- "block3a_expand_activation",
251
- "block2a_expand_activation",
252
- ),
253
- ],
254
- # EfficientNetV2
255
- # Stage layout differs from EfficientNet-Bx.
256
- # Skip layers selected to match decoder resolutions:
257
- # x16 -> x8 -> x4 -> x2
258
- "efficientnetv2s": (
259
- ka.EfficientNetV2S,
260
- ka.efficientnet_v2.preprocess_input,
261
- (
262
- "block6a_expand_conv", # 14x14
263
- "block4a_expand_conv", # 28x28
264
- "block2a_expand_activation", # 56x56
265
- "stem_activation", # 112x112
266
- ),
267
- ),
268
- "efficientnetv2m": (
269
- ka.EfficientNetV2M,
270
- ka.efficientnet_v2.preprocess_input,
271
- (
272
- "block6a_expand_conv",
273
- "block4a_expand_conv",
274
- "block2a_expand_activation",
275
- "stem_activation",
276
- ),
277
- ),
278
- "efficientnetv2l": (
279
- ka.EfficientNetV2L,
280
- ka.efficientnet_v2.preprocess_input,
281
- (
282
- "block6a_expand_conv",
283
- "block4a_expand_conv",
284
- "block2a_expand_activation",
285
- "stem_activation",
286
- ),
287
- ),
288
- }
289
-
290
- @property
291
- def models(self):
292
- """Get the dictionary of available backbones."""
293
- return self._models
294
-
295
- def models_names(self):
296
- """Get the list of available backbone names."""
297
- return list(self.models.keys())
298
-
299
- """Get a backbone model by name."""
300
-
301
- def get_backbone(self, name, *args, **kwargs) -> keras.Model:
302
- """Get a backbone model by name."""
303
- backbone = self._models.get(name)
304
- if backbone is None:
305
- raise ValueError(f"Backbone with name '{name}' is not supported.")
306
-
307
- model_fn, _, _ = backbone
308
- model = model_fn(*args, **kwargs)
309
- return model
310
-
311
- def get_feature_layers(self, name, n=5):
312
- """Get the list of skip layers for a backbone by name."""
313
- return self._models.get(name)[2][:n]
314
-
315
- def get_preprocessing(self, name):
316
- """Get the preprocessing function for a backbone by name."""
317
- return self._models.get(name)[1]
318
-
319
- def get_custom_objects(self, name):
320
- """Get the custom objects for a backbone by name."""
321
- if name == "inceptionresnetv2":
322
- return irv2.get_custom_objects()
323
- return {}
324
-
325
-
326
- Backbones = BackbonesFactory()