segmodels-keras 0.1.0.dev11__tar.gz → 0.1.0.dev12__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 (46) hide show
  1. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/PKG-INFO +1 -1
  2. segmodels_keras-0.1.0.dev12/segmodels_keras/_version.py +2 -0
  3. segmodels_keras-0.1.0.dev12/segmodels_keras/backbones/backbones_factory.py +408 -0
  4. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras.egg-info/PKG-INFO +1 -1
  5. segmodels_keras-0.1.0.dev12/tests/test_backbones.py +57 -0
  6. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/tests/test_models.py +40 -0
  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/tests/test_backbones.py +0 -23
  10. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/LICENSE +0 -0
  11. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/MANIFEST.in +0 -0
  12. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/README.rst +0 -0
  13. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/pyproject.toml +0 -0
  14. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras/__init__.py +0 -0
  15. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras/_compat.py +0 -0
  16. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras/backbones/__init__.py +0 -0
  17. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras/backbones/_common_blocks.py +0 -0
  18. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras/backbones/_weights.py +0 -0
  19. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras/backbones/inception_resnet_v2.py +0 -0
  20. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras/backbones/inception_v3.py +0 -0
  21. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras/backbones/resnet_18_34.py +0 -0
  22. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras/backbones/resnet_common.py +0 -0
  23. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras/base/__init__.py +0 -0
  24. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras/base/functional.py +0 -0
  25. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras/base/objects.py +0 -0
  26. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras/losses.py +0 -0
  27. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras/metrics.py +0 -0
  28. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras/models/__init__.py +0 -0
  29. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras/models/_common_blocks.py +0 -0
  30. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras/models/_utils.py +0 -0
  31. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras/models/fpn.py +0 -0
  32. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras/models/linknet.py +0 -0
  33. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras/models/model_factory.py +0 -0
  34. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras/models/pspnet.py +0 -0
  35. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras/models/unet.py +0 -0
  36. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras/py.typed +0 -0
  37. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras/utils.py +0 -0
  38. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras.egg-info/SOURCES.txt +0 -0
  39. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras.egg-info/dependency_links.txt +0 -0
  40. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras.egg-info/requires.txt +0 -0
  41. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/segmodels_keras.egg-info/top_level.txt +0 -0
  42. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/setup.cfg +0 -0
  43. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/setup.py +0 -0
  44. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/tests/test_helper.py +0 -0
  45. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/tests/test_metrics.py +0 -0
  46. {segmodels_keras-0.1.0.dev11 → segmodels_keras-0.1.0.dev12}/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.dev12
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
@@ -0,0 +1,2 @@
1
+ __versioninfo__: tuple[int, int, int, str] = (0, 1, 0, "dev12")
2
+ __version__: str = ".".join(map(str, __versioninfo__))
@@ -0,0 +1,408 @@
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
+ "efficientnetv2s": (
311
+ ka.EfficientNetV2S,
312
+ ka.efficientnet_v2.preprocess_input,
313
+ (
314
+ "block6a_expand_conv", # 32x32x960
315
+ "block4a_expand_conv", # 64x64x256
316
+ "block2a_expand_activation", # 128x128x96
317
+ "stem_activation", # 256x256x24
318
+ ),
319
+ ),
320
+ "efficientnetv2s-ss": (
321
+ ka.EfficientNetV2S,
322
+ ka.efficientnet_v2.preprocess_input,
323
+ (
324
+ "block5i_add", # 32x32x160
325
+ "block3d_add", # 64x64x64
326
+ "block2d_add", # 128x128x48
327
+ "stem_activation", # 256x256x24
328
+ ),
329
+ ),
330
+ "efficientnetv2m": (
331
+ ka.EfficientNetV2M,
332
+ ka.efficientnet_v2.preprocess_input,
333
+ (
334
+ "block6a_expand_conv", # 32x32x1056
335
+ "block4a_expand_conv", # 64x64x320
336
+ "block2a_expand_activation", # 128x128x96
337
+ "stem_activation", # 256x256x24
338
+ ),
339
+ ),
340
+ "efficientnetv2m-ss": (
341
+ ka.EfficientNetV2M,
342
+ ka.efficientnet_v2.preprocess_input,
343
+ (
344
+ "block5n_add", # 32x32x176
345
+ "block3e_add", # 64x64x80
346
+ "block2e_add", # 128x128x48
347
+ "stem_activation", # 256x256x24
348
+ ),
349
+ ),
350
+ "efficientnetv2l": (
351
+ ka.EfficientNetV2L,
352
+ ka.efficientnet_v2.preprocess_input,
353
+ (
354
+ "block6a_expand_conv", # 32x32x1344
355
+ "block4a_expand_conv", # 64x64x384
356
+ "block2a_expand_activation", # 128x128x128
357
+ "stem_activation", # 256x256x32
358
+ ),
359
+ ),
360
+ "efficientnetv2l-ss": (
361
+ ka.EfficientNetV2L,
362
+ ka.efficientnet_v2.preprocess_input,
363
+ (
364
+ "block5s_add", # 32x32x224
365
+ "block3g_add", # 64x64x96
366
+ "block2g_add", # 128x128x64
367
+ "stem_activation", # 256x256x32
368
+ ),
369
+ ),
370
+ }
371
+
372
+ @property
373
+ def models(self):
374
+ """Get the dictionary of available backbones."""
375
+ return self._models
376
+
377
+ def models_names(self):
378
+ """Get the list of available backbone names."""
379
+ return list(self.models.keys())
380
+
381
+ """Get a backbone model by name."""
382
+
383
+ def get_backbone(self, name, *args, **kwargs) -> keras.Model:
384
+ """Get a backbone model by name."""
385
+ backbone = self._models.get(name)
386
+ if backbone is None:
387
+ raise ValueError(f"Backbone with name '{name}' is not supported.")
388
+
389
+ model_fn, _, _ = backbone
390
+ model = model_fn(*args, **kwargs)
391
+ return model
392
+
393
+ def get_feature_layers(self, name, n=5):
394
+ """Get the list of skip layers for a backbone by name."""
395
+ return self._models.get(name)[2][:n]
396
+
397
+ def get_preprocessing(self, name):
398
+ """Get the preprocessing function for a backbone by name."""
399
+ return self._models.get(name)[1]
400
+
401
+ def get_custom_objects(self, name):
402
+ """Get the custom objects for a backbone by name."""
403
+ if name == "inceptionresnetv2":
404
+ return irv2.get_custom_objects()
405
+ return {}
406
+
407
+
408
+ 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.dev12
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
@@ -0,0 +1,57 @@
1
+ """Tests on the backbone factory and backbone retrieval."""
2
+
3
+ import pytest
4
+
5
+ from segmodels_keras.backbones.backbones_factory import BackbonesFactory
6
+
7
+
8
+ def test_backbones_factory():
9
+ factory = BackbonesFactory()
10
+ assert len(factory.models) > 0
11
+
12
+ for name in factory.models.keys():
13
+ model_fn, preprocess_fn, layers = factory.models[name]
14
+ assert callable(model_fn)
15
+ assert callable(preprocess_fn)
16
+ assert isinstance(layers, tuple)
17
+
18
+
19
+ def test_get_backbone_unknown():
20
+ factory = BackbonesFactory()
21
+
22
+ with pytest.raises(ValueError):
23
+ factory.get_backbone("unknown")
24
+
25
+
26
+ @pytest.mark.parametrize(
27
+ "backbone_name, expected_shapes",
28
+ [
29
+ (
30
+ "efficientnetv2s-ss",
31
+ ((4, 4, 160), (8, 8, 64), (16, 16, 48), (32, 32, 24)),
32
+ ),
33
+ (
34
+ "efficientnetv2m-ss",
35
+ ((4, 4, 176), (8, 8, 80), (16, 16, 48), (32, 32, 24)),
36
+ ),
37
+ (
38
+ "efficientnetv2l-ss",
39
+ ((4, 4, 224), (8, 8, 96), (16, 16, 64), (32, 32, 32)),
40
+ ),
41
+ ],
42
+ )
43
+ def test_efficientnetv2_compact_skip_shapes(backbone_name, expected_shapes):
44
+ factory = BackbonesFactory()
45
+ model = factory.get_backbone(
46
+ backbone_name,
47
+ include_top=False,
48
+ weights=None,
49
+ input_shape=(64, 64, 3),
50
+ )
51
+
52
+ feature_shapes = tuple(
53
+ tuple(int(size) for size in model.get_layer(layer_name).output.shape[1:])
54
+ for layer_name in factory.get_feature_layers(backbone_name, n=4)
55
+ )
56
+
57
+ assert feature_shapes == expected_shapes
@@ -31,6 +31,9 @@ def get_test_backbones():
31
31
  "inceptionresnetv2",
32
32
  "efficientnetb0",
33
33
  "efficientnetv2s",
34
+ "efficientnetv2s-ss",
35
+ "efficientnetv2m-ss",
36
+ "efficientnetv2l-ss",
34
37
  ]
35
38
  else:
36
39
  return get_available_backbone_names()
@@ -95,6 +98,43 @@ def test_get_model_invalid_name():
95
98
  get_model("invalid_model")
96
99
 
97
100
 
101
+ @pytest.mark.parametrize(
102
+ "standard_backbone, compact_backbone, expected_parameter_reduction",
103
+ [
104
+ ("efficientnetv2s", "efficientnetv2s-ss", 2_092_032),
105
+ ("efficientnetv2m", "efficientnetv2m-ss", 2_331_648),
106
+ ("efficientnetv2l", "efficientnetv2l-ss", 2_949_120),
107
+ ],
108
+ )
109
+ @keras_test
110
+ def test_efficientnetv2_compact_skips_reduce_unet_parameters(
111
+ standard_backbone, compact_backbone, expected_parameter_reduction
112
+ ):
113
+ input_shape = (32, 32, 3)
114
+ standard_model = get_model(
115
+ "unet",
116
+ backbone_name=standard_backbone,
117
+ input_shape=input_shape,
118
+ encoder_weights=None,
119
+ )
120
+ standard_params = standard_model.count_params()
121
+ standard_output_shape = standard_model.output_shape
122
+ del standard_model
123
+ keras.backend.clear_session()
124
+
125
+ compact_model = get_model(
126
+ "unet",
127
+ backbone_name=compact_backbone,
128
+ input_shape=input_shape,
129
+ encoder_weights=None,
130
+ )
131
+
132
+ assert compact_model.output_shape == standard_output_shape
133
+ assert (
134
+ standard_params - compact_model.count_params() == expected_parameter_reduction
135
+ )
136
+
137
+
98
138
  def test_get_model_weights_notop_keras2():
99
139
  """Test loading a Keras v2 style HDF5 weight file."""
100
140
  weights_path = data_dir / "mobilenetv2+linknet_notop_k2.weights.h5"
@@ -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()
@@ -1,23 +0,0 @@
1
- """Tests on the backbone factory and backbone retrieval."""
2
-
3
- import pytest
4
-
5
- from segmodels_keras.backbones.backbones_factory import BackbonesFactory
6
-
7
-
8
- def test_backbones_factory():
9
- factory = BackbonesFactory()
10
- assert len(factory.models) > 0
11
-
12
- for name in factory.models.keys():
13
- model_fn, preprocess_fn, layers = factory.models[name]
14
- assert callable(model_fn)
15
- assert callable(preprocess_fn)
16
- assert isinstance(layers, tuple)
17
-
18
-
19
- def test_get_backbone_unknown():
20
- factory = BackbonesFactory()
21
-
22
- with pytest.raises(ValueError):
23
- factory.get_backbone("unknown")