segmodels-keras 0.1.0.dev9__tar.gz → 0.1.0.dev11__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.
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/PKG-INFO +1 -1
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/pyproject.toml +5 -1
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/segmodels_keras/__init__.py +25 -18
- segmodels_keras-0.1.0.dev11/segmodels_keras/_compat.py +6 -0
- segmodels_keras-0.1.0.dev11/segmodels_keras/_version.py +2 -0
- segmodels_keras-0.1.0.dev11/segmodels_keras/backbones/__init__.py +1 -0
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/segmodels_keras/backbones/_common_blocks.py +6 -3
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/segmodels_keras/backbones/_weights.py +2 -1
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/segmodels_keras/backbones/backbones_factory.py +39 -6
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/segmodels_keras/backbones/inception_resnet_v2.py +10 -5
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/segmodels_keras/backbones/inception_v3.py +10 -3
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/segmodels_keras/backbones/resnet_18_34.py +5 -4
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/segmodels_keras/base/__init__.py +2 -0
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/segmodels_keras/base/functional.py +44 -15
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/segmodels_keras/base/objects.py +40 -24
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/segmodels_keras/losses.py +47 -37
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/segmodels_keras/metrics.py +59 -54
- segmodels_keras-0.1.0.dev11/segmodels_keras/models/__init__.py +5 -0
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/segmodels_keras/models/_common_blocks.py +1 -2
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/segmodels_keras/models/_utils.py +1 -1
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/segmodels_keras/models/fpn.py +65 -49
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/segmodels_keras/models/linknet.py +72 -48
- segmodels_keras-0.1.0.dev11/segmodels_keras/models/model_factory.py +96 -0
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/segmodels_keras/models/pspnet.py +64 -48
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/segmodels_keras/models/unet.py +79 -60
- segmodels_keras-0.1.0.dev11/segmodels_keras/utils.py +314 -0
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/segmodels_keras.egg-info/PKG-INFO +1 -1
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/segmodels_keras.egg-info/SOURCES.txt +3 -0
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/setup.py +4 -1
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/tests/test_backbones.py +2 -0
- segmodels_keras-0.1.0.dev11/tests/test_helper.py +5 -0
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/tests/test_metrics.py +2 -0
- segmodels_keras-0.1.0.dev11/tests/test_models.py +117 -0
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/tests/test_utils.py +17 -23
- segmodels_keras-0.1.0.dev9/segmodels_keras/_compat.py +0 -123
- segmodels_keras-0.1.0.dev9/segmodels_keras/_version.py +0 -2
- segmodels_keras-0.1.0.dev9/segmodels_keras/models/__init__.py +0 -0
- segmodels_keras-0.1.0.dev9/segmodels_keras/utils.py +0 -117
- segmodels_keras-0.1.0.dev9/tests/test_models.py +0 -128
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/LICENSE +0 -0
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/MANIFEST.in +0 -0
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/README.rst +0 -0
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/segmodels_keras/backbones/resnet_common.py +0 -0
- /segmodels_keras-0.1.0.dev9/segmodels_keras/backbones/__init__.py → /segmodels_keras-0.1.0.dev11/segmodels_keras/py.typed +0 -0
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/segmodels_keras.egg-info/dependency_links.txt +0 -0
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/segmodels_keras.egg-info/requires.txt +0 -0
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/segmodels_keras.egg-info/top_level.txt +0 -0
- {segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/setup.cfg +0 -0
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
[[tool.mypy.overrides]]
|
|
2
|
+
module = "h5py.*,keras.*,six.*,setuptools.*,sphinx_rtd_theme.*,tensorflow.*"
|
|
3
|
+
ignore_missing_imports = true
|
|
4
|
+
|
|
1
5
|
[tool.pyright]
|
|
2
6
|
exclude = ["local_ignore", "**/node_modules", "**/__pycache__", "**/.*"]
|
|
3
7
|
ignore = ["*"]
|
|
@@ -33,7 +37,7 @@ select = [
|
|
|
33
37
|
"NPY", # NumPy-specific rules
|
|
34
38
|
"E", # pycodestyle errors
|
|
35
39
|
"W", # pycodestyle warnings
|
|
36
|
-
|
|
40
|
+
"D", # pydocstyle
|
|
37
41
|
"F", # pyflakes
|
|
38
42
|
"PLC", # pylint convention
|
|
39
43
|
"PLE", # pylint error
|
|
@@ -1,35 +1,39 @@
|
|
|
1
|
+
"""Segmentation Models for Keras."""
|
|
2
|
+
|
|
1
3
|
import functools # noqa: I001
|
|
2
4
|
import os
|
|
5
|
+
from typing import Any
|
|
6
|
+
from collections.abc import Callable
|
|
3
7
|
|
|
4
8
|
# __version__ should be defined as soon as possible
|
|
5
9
|
from ._version import __version__
|
|
6
10
|
|
|
7
11
|
from . import base
|
|
8
12
|
|
|
9
|
-
_KERAS_FRAMEWORK_NAME = "keras"
|
|
10
|
-
_TF_KERAS_FRAMEWORK_NAME = "tf.keras"
|
|
13
|
+
_KERAS_FRAMEWORK_NAME: str = "keras"
|
|
14
|
+
_TF_KERAS_FRAMEWORK_NAME: str = "tf.keras"
|
|
11
15
|
|
|
12
|
-
_DEFAULT_KERAS_FRAMEWORK = _KERAS_FRAMEWORK_NAME
|
|
13
|
-
_KERAS_FRAMEWORK = None
|
|
14
|
-
_KERAS_BACKEND = None
|
|
15
|
-
_KERAS_LAYERS = None
|
|
16
|
-
_KERAS_MODELS = None
|
|
17
|
-
_KERAS_UTILS = None
|
|
18
|
-
_KERAS_LOSSES = None
|
|
16
|
+
_DEFAULT_KERAS_FRAMEWORK: str = _KERAS_FRAMEWORK_NAME
|
|
17
|
+
_KERAS_FRAMEWORK: str | None = None
|
|
18
|
+
_KERAS_BACKEND: Any = None
|
|
19
|
+
_KERAS_LAYERS: Any = None
|
|
20
|
+
_KERAS_MODELS: Any = None
|
|
21
|
+
_KERAS_UTILS: Any = None
|
|
22
|
+
_KERAS_LOSSES: Any = None
|
|
19
23
|
|
|
20
24
|
|
|
21
|
-
def inject_global_losses(func):
|
|
25
|
+
def inject_global_losses(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
22
26
|
@functools.wraps(func)
|
|
23
|
-
def wrapper(*args, **kwargs):
|
|
27
|
+
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
24
28
|
kwargs["losses"] = _KERAS_LOSSES
|
|
25
29
|
return func(*args, **kwargs)
|
|
26
30
|
|
|
27
31
|
return wrapper
|
|
28
32
|
|
|
29
33
|
|
|
30
|
-
def filter_kwargs(func):
|
|
34
|
+
def filter_kwargs(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
31
35
|
@functools.wraps(func)
|
|
32
|
-
def wrapper(*args, **kwargs):
|
|
36
|
+
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
33
37
|
new_kwargs = {
|
|
34
38
|
k: v
|
|
35
39
|
for k, v in kwargs.items()
|
|
@@ -40,13 +44,13 @@ def filter_kwargs(func):
|
|
|
40
44
|
return wrapper
|
|
41
45
|
|
|
42
46
|
|
|
43
|
-
def framework():
|
|
44
|
-
"""Return name of Segmentation Models framework"""
|
|
47
|
+
def framework() -> str | None:
|
|
48
|
+
"""Return name of Segmentation Models framework."""
|
|
45
49
|
return _KERAS_FRAMEWORK
|
|
46
50
|
|
|
47
51
|
|
|
48
|
-
def set_framework(name):
|
|
49
|
-
"""Set framework for Segmentation Models
|
|
52
|
+
def set_framework(name: str) -> None:
|
|
53
|
+
"""Set framework for Segmentation Models.
|
|
50
54
|
|
|
51
55
|
Args:
|
|
52
56
|
name (str): one of ``keras``, ``tf.keras``, case insensitive.
|
|
@@ -108,13 +112,15 @@ from . import losses, metrics, utils
|
|
|
108
112
|
from .backbones.backbones_factory import Backbones
|
|
109
113
|
from .models.fpn import FPN
|
|
110
114
|
from .models.linknet import Linknet
|
|
115
|
+
from .models.model_factory import get_model
|
|
111
116
|
from .models.pspnet import PSPNet
|
|
112
117
|
from .models.unet import Unet
|
|
113
118
|
|
|
114
119
|
get_available_backbone_names = Backbones.models_names
|
|
115
120
|
|
|
116
121
|
|
|
117
|
-
def get_preprocessing(name):
|
|
122
|
+
def get_preprocessing(name: str) -> Callable[[Any], Any]:
|
|
123
|
+
"""Get the preprocessing function for a backbone by name."""
|
|
118
124
|
return Backbones.get_preprocessing(name)
|
|
119
125
|
|
|
120
126
|
|
|
@@ -126,6 +132,7 @@ __all__ = [
|
|
|
126
132
|
"__version__",
|
|
127
133
|
"framework",
|
|
128
134
|
"get_available_backbone_names",
|
|
135
|
+
"get_model",
|
|
129
136
|
"get_preprocessing",
|
|
130
137
|
"losses",
|
|
131
138
|
"metrics",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Backbones package."""
|
|
@@ -44,6 +44,7 @@ def GroupConv2D(
|
|
|
44
44
|
If you don't specify anything, no activation is applied
|
|
45
45
|
(ie. "linear" activation: a(x) = x).
|
|
46
46
|
padding: one of "valid" or "same" (case-insensitive).
|
|
47
|
+
kwargs: additional keyword arguments (unused).
|
|
47
48
|
|
|
48
49
|
Input shape:
|
|
49
50
|
4D tensor with shape: (batch, rows, cols, channels) if data_format is
|
|
@@ -97,12 +98,14 @@ def expand_dims(x, channels_axis):
|
|
|
97
98
|
|
|
98
99
|
|
|
99
100
|
def ChannelSE(reduction=16, **kwargs): # noqa: ARG001
|
|
100
|
-
"""
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
"""Squeeze and Excitation block.
|
|
102
|
+
|
|
103
|
+
Reimplementation inspired by
|
|
104
|
+
https://github.com/Cadene/pretrained-models.pytorch/blob/master/pretrainedmodels/models/senet.py.
|
|
103
105
|
|
|
104
106
|
Args:
|
|
105
107
|
reduction: channels squeeze factor
|
|
108
|
+
kwargs: additional keyword arguments (unused).
|
|
106
109
|
|
|
107
110
|
"""
|
|
108
111
|
channels_axis = 3 if backend.image_data_format() == "channels_last" else 1
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
"""Backbones factory module."""
|
|
2
|
+
|
|
3
|
+
import keras
|
|
1
4
|
import keras.applications as ka
|
|
2
5
|
|
|
3
6
|
from . import inception_resnet_v2 as irv2
|
|
@@ -24,14 +27,14 @@ class BackbonesFactory:
|
|
|
24
27
|
resnet_18_34.ResNet18,
|
|
25
28
|
resnet_18_34.preprocess_input,
|
|
26
29
|
# resnet18: layer1=2, layer2=2, layer3=2, layer4=2 blocks
|
|
27
|
-
# backbone output = conv5_block2_out (512ch); skips are the 4 stages before
|
|
30
|
+
# backbone output = conv5_block2_out (512ch); skips are the 4 stages before
|
|
28
31
|
("conv4_block2_out", "conv3_block2_out", "conv2_block2_out", "conv1_relu"),
|
|
29
32
|
),
|
|
30
33
|
"resnet34": (
|
|
31
34
|
resnet_18_34.ResNet34,
|
|
32
35
|
resnet_18_34.preprocess_input,
|
|
33
36
|
# resnet34: layer1=3, layer2=4, layer3=6, layer4=3 blocks
|
|
34
|
-
# backbone output = conv5_block3_out (512ch); skips are the 4 stages before
|
|
37
|
+
# backbone output = conv5_block3_out (512ch); skips are the 4 stages before
|
|
35
38
|
("conv4_block6_out", "conv3_block4_out", "conv2_block3_out", "conv1_relu"),
|
|
36
39
|
),
|
|
37
40
|
# ResNets > 50 layers are available via keras.applications, so use those.
|
|
@@ -249,27 +252,54 @@ class BackbonesFactory:
|
|
|
249
252
|
),
|
|
250
253
|
],
|
|
251
254
|
# EfficientNetV2
|
|
252
|
-
#
|
|
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
|
+
),
|
|
253
268
|
"efficientnetv2m": (
|
|
254
269
|
ka.EfficientNetV2M,
|
|
255
270
|
ka.efficientnet_v2.preprocess_input,
|
|
256
271
|
(
|
|
257
272
|
"block6a_expand_conv",
|
|
258
273
|
"block4a_expand_conv",
|
|
259
|
-
"
|
|
260
|
-
"
|
|
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",
|
|
261
286
|
),
|
|
262
287
|
),
|
|
263
288
|
}
|
|
264
289
|
|
|
265
290
|
@property
|
|
266
291
|
def models(self):
|
|
292
|
+
"""Get the dictionary of available backbones."""
|
|
267
293
|
return self._models
|
|
268
294
|
|
|
269
295
|
def models_names(self):
|
|
296
|
+
"""Get the list of available backbone names."""
|
|
270
297
|
return list(self.models.keys())
|
|
271
298
|
|
|
272
|
-
|
|
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."""
|
|
273
303
|
backbone = self._models.get(name)
|
|
274
304
|
if backbone is None:
|
|
275
305
|
raise ValueError(f"Backbone with name '{name}' is not supported.")
|
|
@@ -279,12 +309,15 @@ class BackbonesFactory:
|
|
|
279
309
|
return model
|
|
280
310
|
|
|
281
311
|
def get_feature_layers(self, name, n=5):
|
|
312
|
+
"""Get the list of skip layers for a backbone by name."""
|
|
282
313
|
return self._models.get(name)[2][:n]
|
|
283
314
|
|
|
284
315
|
def get_preprocessing(self, name):
|
|
316
|
+
"""Get the preprocessing function for a backbone by name."""
|
|
285
317
|
return self._models.get(name)[1]
|
|
286
318
|
|
|
287
319
|
def get_custom_objects(self, name):
|
|
320
|
+
"""Get the custom objects for a backbone by name."""
|
|
288
321
|
if name == "inceptionresnetv2":
|
|
289
322
|
return irv2.get_custom_objects()
|
|
290
323
|
return {}
|
|
@@ -38,8 +38,7 @@ from keras import backend, layers, models
|
|
|
38
38
|
from keras.applications import imagenet_utils
|
|
39
39
|
from keras.utils import get_file, get_source_inputs
|
|
40
40
|
|
|
41
|
-
from
|
|
42
|
-
|
|
41
|
+
from ..utils import load_weights
|
|
43
42
|
from .resnet_common import _obtain_input_shape
|
|
44
43
|
|
|
45
44
|
BASE_WEIGHT_URL = (
|
|
@@ -315,16 +314,18 @@ def conv2d_bn(
|
|
|
315
314
|
|
|
316
315
|
|
|
317
316
|
class CustomScaleLayer(layers.Layer):
|
|
318
|
-
|
|
317
|
+
"""Custom layer to scale the residuals before adding them to the shortcut branch."""
|
|
318
|
+
|
|
319
|
+
def __init__(self, scale, **kwargs): # noqa: D107
|
|
319
320
|
super().__init__(**kwargs)
|
|
320
321
|
self.scale = scale
|
|
321
322
|
|
|
322
|
-
def get_config(self):
|
|
323
|
+
def get_config(self): # noqa: D102
|
|
323
324
|
config = super().get_config()
|
|
324
325
|
config.update({"scale": self.scale})
|
|
325
326
|
return config
|
|
326
327
|
|
|
327
|
-
def call(self, inputs):
|
|
328
|
+
def call(self, inputs): # noqa: D102
|
|
328
329
|
return inputs[0] + inputs[1] * self.scale
|
|
329
330
|
|
|
330
331
|
|
|
@@ -417,6 +418,9 @@ def preprocess_input(x, data_format=None):
|
|
|
417
418
|
|
|
418
419
|
Args:
|
|
419
420
|
x: a 4D numpy array consists of RGB values within [0, 255].
|
|
421
|
+
data_format: image data format, either "channels_first" or
|
|
422
|
+
"channels_last". Defaults to the value found in
|
|
423
|
+
`keras.backend.image_data_format()`.
|
|
420
424
|
|
|
421
425
|
Returns:
|
|
422
426
|
Preprocessed array.
|
|
@@ -444,4 +448,5 @@ def decode_predictions(preds, top=5):
|
|
|
444
448
|
|
|
445
449
|
|
|
446
450
|
def get_custom_objects():
|
|
451
|
+
"""Returns a dictionary containing the custom objects used by the model."""
|
|
447
452
|
return {"CustomScaleLayer": CustomScaleLayer}
|
{segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/segmodels_keras/backbones/inception_v3.py
RENAMED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
"""Inception V3 model for Keras.
|
|
2
|
+
|
|
2
3
|
Note that the input image format for this model is different than for
|
|
3
4
|
the VGG16 and ResNet models (299x299 instead of 224x224),
|
|
4
5
|
and that the input preprocessing function is also different (same as Xception).
|
|
5
|
-
|
|
6
|
+
|
|
7
|
+
References:
|
|
8
|
+
----------
|
|
6
9
|
- [Rethinking the Inception Architecture for Computer Vision](
|
|
7
|
-
http://arxiv.org/abs/1512.00567) (CVPR 2016)
|
|
10
|
+
http://arxiv.org/abs/1512.00567) (CVPR 2016).
|
|
8
11
|
"""
|
|
9
12
|
|
|
10
13
|
import os
|
|
@@ -14,7 +17,7 @@ from keras import backend, layers, models
|
|
|
14
17
|
from keras import utils as keras_utils
|
|
15
18
|
from keras.applications import imagenet_utils
|
|
16
19
|
|
|
17
|
-
from
|
|
20
|
+
from ..utils import load_weights
|
|
18
21
|
|
|
19
22
|
WEIGHTS_PATH = (
|
|
20
23
|
"https://github.com/fchollet/deep-learning-models/"
|
|
@@ -30,6 +33,7 @@ WEIGHTS_PATH_NO_TOP = (
|
|
|
30
33
|
|
|
31
34
|
def conv2d_bn(x, filters, num_row, num_col, padding="same", strides=(1, 1), name=None):
|
|
32
35
|
"""Utility function to apply conv + BN.
|
|
36
|
+
|
|
33
37
|
# Arguments
|
|
34
38
|
x: input tensor.
|
|
35
39
|
filters: filters in `Conv2D`.
|
|
@@ -76,9 +80,11 @@ def InceptionV3(
|
|
|
76
80
|
**kwargs, # noqa: ARG001
|
|
77
81
|
):
|
|
78
82
|
"""Instantiates the Inception v3 architecture.
|
|
83
|
+
|
|
79
84
|
Optionally loads weights pre-trained on ImageNet.
|
|
80
85
|
Note that the data format convention used by the model is
|
|
81
86
|
the one specified in your Keras config at `~/.keras/keras.json`.
|
|
87
|
+
|
|
82
88
|
# Arguments
|
|
83
89
|
include_top: whether to include the fully-connected
|
|
84
90
|
layer at the top of the network.
|
|
@@ -372,6 +378,7 @@ def InceptionV3(
|
|
|
372
378
|
|
|
373
379
|
def preprocess_input(x, **kwargs):
|
|
374
380
|
"""Preprocesses a numpy array encoding a batch of images.
|
|
381
|
+
|
|
375
382
|
# Arguments
|
|
376
383
|
x: a 4D numpy array consists of RGB values within [0, 255].
|
|
377
384
|
# Returns
|
{segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/segmodels_keras/backbones/resnet_18_34.py
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
"""ResNet18 and ResNet34 Keras models.
|
|
2
2
|
|
|
3
3
|
Complements keras.applications (which provides ResNet50+) with BasicBlock-based
|
|
4
4
|
ResNet18 and ResNet34. The implementation follows the keras.applications structure
|
|
@@ -38,8 +38,7 @@ from keras import backend, layers, models
|
|
|
38
38
|
from keras.applications import imagenet_utils
|
|
39
39
|
from keras.utils import get_source_inputs
|
|
40
40
|
|
|
41
|
-
from
|
|
42
|
-
|
|
41
|
+
from ..utils import load_weights
|
|
43
42
|
from .resnet_common import _obtain_input_shape
|
|
44
43
|
|
|
45
44
|
momentum = 0.99
|
|
@@ -110,7 +109,6 @@ def ResNet(
|
|
|
110
109
|
Returns:
|
|
111
110
|
A ``keras.Model`` instance.
|
|
112
111
|
"""
|
|
113
|
-
|
|
114
112
|
# Determine proper input shape
|
|
115
113
|
input_shape = _obtain_input_shape(
|
|
116
114
|
input_shape,
|
|
@@ -359,6 +357,9 @@ def preprocess_input(x, data_format=None):
|
|
|
359
357
|
|
|
360
358
|
Args:
|
|
361
359
|
x: a 4D numpy array consists of RGB values within [0, 255].
|
|
360
|
+
data_format: image data format, either "channels_first" or
|
|
361
|
+
"channels_last". Defaults to the value found in
|
|
362
|
+
`keras.backend.image_data_format()`.
|
|
362
363
|
|
|
363
364
|
Returns:
|
|
364
365
|
Preprocessed array.
|
{segmodels_keras-0.1.0.dev9 → segmodels_keras-0.1.0.dev11}/segmodels_keras/base/functional.py
RENAMED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
"""Functional utilities for segmentation models."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
1
5
|
import keras
|
|
2
6
|
|
|
3
7
|
from segmodels_keras._compat import KERAS_GTE_3
|
|
@@ -7,7 +11,7 @@ if KERAS_GTE_3:
|
|
|
7
11
|
else:
|
|
8
12
|
from keras import backend as ops
|
|
9
13
|
|
|
10
|
-
SMOOTH = 1e-5
|
|
14
|
+
SMOOTH: float = 1e-5
|
|
11
15
|
|
|
12
16
|
|
|
13
17
|
# ----------------------------------------------------------------
|
|
@@ -15,7 +19,7 @@ SMOOTH = 1e-5
|
|
|
15
19
|
# ----------------------------------------------------------------
|
|
16
20
|
|
|
17
21
|
|
|
18
|
-
def _gather_channels(x, indexes, **kwargs):
|
|
22
|
+
def _gather_channels(x: Any, indexes: Any, **kwargs: Any) -> Any:
|
|
19
23
|
backend = kwargs["backend"]
|
|
20
24
|
|
|
21
25
|
"""Slice tensor along channels axis by given indexes"""
|
|
@@ -30,7 +34,8 @@ def _gather_channels(x, indexes, **kwargs):
|
|
|
30
34
|
return x
|
|
31
35
|
|
|
32
36
|
|
|
33
|
-
def get_reduce_axes(per_image, **kwargs):
|
|
37
|
+
def get_reduce_axes(per_image: bool, **kwargs: Any) -> list[int]:
|
|
38
|
+
"""Get axes for reduction operations."""
|
|
34
39
|
backend = kwargs["backend"]
|
|
35
40
|
|
|
36
41
|
axes = [1, 2] if backend.image_data_format() == "channels_last" else [2, 3]
|
|
@@ -39,17 +44,20 @@ def get_reduce_axes(per_image, **kwargs):
|
|
|
39
44
|
return axes
|
|
40
45
|
|
|
41
46
|
|
|
42
|
-
def gather_channels(
|
|
43
|
-
|
|
47
|
+
def gather_channels(
|
|
48
|
+
*xs: Any, indexes: int | list[int] | None = None, **kwargs: Any
|
|
49
|
+
) -> tuple[Any, ...] | tuple[Any]:
|
|
50
|
+
"""Slice tensors along channels axis by given indexes."""
|
|
44
51
|
if indexes is None:
|
|
45
52
|
return xs
|
|
46
53
|
elif isinstance(indexes, (int)):
|
|
47
54
|
indexes = [indexes]
|
|
48
|
-
xs =
|
|
55
|
+
xs = tuple(_gather_channels(x, indexes=indexes, **kwargs) for x in xs)
|
|
49
56
|
return xs
|
|
50
57
|
|
|
51
58
|
|
|
52
59
|
def round_if_needed(x, threshold, **kwargs):
|
|
60
|
+
"""Round predictions if threshold is not None."""
|
|
53
61
|
if threshold is not None:
|
|
54
62
|
x = ops.greater(x, threshold)
|
|
55
63
|
if not KERAS_GTE_3:
|
|
@@ -61,6 +69,10 @@ def round_if_needed(x, threshold, **kwargs):
|
|
|
61
69
|
|
|
62
70
|
|
|
63
71
|
def average(x, per_image=False, class_weights=None, **kwargs): # noqa: ARG001
|
|
72
|
+
"""Calculate the average of a tensor.
|
|
73
|
+
|
|
74
|
+
Optionally per image and with class weights.
|
|
75
|
+
"""
|
|
64
76
|
if per_image:
|
|
65
77
|
x = ops.mean(x, axis=0)
|
|
66
78
|
if class_weights is not None:
|
|
@@ -83,12 +95,14 @@ def iou_score(
|
|
|
83
95
|
threshold=None,
|
|
84
96
|
**kwargs,
|
|
85
97
|
):
|
|
86
|
-
r"""The `Jaccard index`_
|
|
87
|
-
|
|
88
|
-
|
|
98
|
+
r"""The `Jaccard index`_.
|
|
99
|
+
|
|
100
|
+
Also known as Intersection over Union and the Jaccard similarity coefficient
|
|
101
|
+
(originally coined coefficient de communauté by Paul Jaccard),
|
|
102
|
+
is a statistic used for comparing the similarity and diversity of sample
|
|
89
103
|
sets. The Jaccard coefficient measures similarity between finite sample sets,
|
|
90
104
|
and is defined as the size of the intersection divided by the size of the union of
|
|
91
|
-
the sample sets
|
|
105
|
+
the sample sets.
|
|
92
106
|
|
|
93
107
|
.. math:: J(A, B) = \frac{A \cap B}{A \cup B}
|
|
94
108
|
|
|
@@ -103,6 +117,8 @@ def iou_score(
|
|
|
103
117
|
else over whole batch
|
|
104
118
|
threshold: value to round predictions (use ``>`` comparison), if ``None``
|
|
105
119
|
prediction will not be round
|
|
120
|
+
kwargs: additional arguments, for example, ``backend`` is required for some
|
|
121
|
+
operations
|
|
106
122
|
|
|
107
123
|
Returns:
|
|
108
124
|
IoU/Jaccard score in range [0, 1]
|
|
@@ -135,9 +151,10 @@ def f_score(
|
|
|
135
151
|
threshold=None,
|
|
136
152
|
**kwargs,
|
|
137
153
|
):
|
|
138
|
-
r"""The F-score (Dice coefficient)
|
|
139
|
-
|
|
140
|
-
|
|
154
|
+
r"""The F-score (Dice coefficient).
|
|
155
|
+
|
|
156
|
+
Can be interpreted as a weighted average of the precision and recall, where an
|
|
157
|
+
F-score reaches its best value at 1 and worst score at 0.
|
|
141
158
|
The relative contribution of ``precision`` and ``recall`` to the F1-score are equal.
|
|
142
159
|
|
|
143
160
|
The formula for the F score is:
|
|
@@ -167,6 +184,8 @@ def f_score(
|
|
|
167
184
|
else over whole batch
|
|
168
185
|
threshold: value to round predictions (use ``>`` comparison), if ``None``
|
|
169
186
|
prediction will not be round
|
|
187
|
+
kwargs: additional arguments, for example, ``backend`` is required for some
|
|
188
|
+
operations
|
|
170
189
|
|
|
171
190
|
Returns:
|
|
172
191
|
F-score in range [0, 1]
|
|
@@ -220,6 +239,8 @@ def precision(
|
|
|
220
239
|
threshold: Float value to round predictions (use ``>`` comparison), if ``None``
|
|
221
240
|
prediction will not be round.
|
|
222
241
|
name: Optional string, if ``None`` default ``precision`` name is used.
|
|
242
|
+
kwargs: additional arguments, for example, ``backend`` is required for some
|
|
243
|
+
operations
|
|
223
244
|
|
|
224
245
|
Returns:
|
|
225
246
|
float: precision score
|
|
@@ -269,6 +290,8 @@ def recall(
|
|
|
269
290
|
threshold: Float value to round predictions (use ``>`` comparison), if ``None``
|
|
270
291
|
prediction will not be round.
|
|
271
292
|
name: Optional string, if ``None`` default ``precision`` name is used.
|
|
293
|
+
kwargs: additional arguments, for example, ``backend`` is required for some
|
|
294
|
+
operations
|
|
272
295
|
|
|
273
296
|
Returns:
|
|
274
297
|
float: recall score
|
|
@@ -292,6 +315,7 @@ def recall(
|
|
|
292
315
|
|
|
293
316
|
|
|
294
317
|
def categorical_crossentropy(gt, pr, class_weights=1.0, class_indexes=None, **kwargs):
|
|
318
|
+
"""Calculate categorical crossentropy loss between gt and pr tensors."""
|
|
295
319
|
backend = kwargs["backend"]
|
|
296
320
|
|
|
297
321
|
gt, pr = gather_channels(gt, pr, indexes=class_indexes, **kwargs)
|
|
@@ -309,11 +333,12 @@ def categorical_crossentropy(gt, pr, class_weights=1.0, class_indexes=None, **kw
|
|
|
309
333
|
|
|
310
334
|
|
|
311
335
|
def binary_crossentropy(gt, pr, **kwargs): # noqa: ARG001
|
|
336
|
+
"""Calculate binary crossentropy loss between gt and pr tensors."""
|
|
312
337
|
return ops.mean(ops.binary_crossentropy(gt, pr))
|
|
313
338
|
|
|
314
339
|
|
|
315
340
|
def categorical_focal_loss(gt, pr, gamma=2.0, alpha=0.25, class_indexes=None, **kwargs):
|
|
316
|
-
r"""Implementation of Focal Loss from the paper in multiclass classification
|
|
341
|
+
r"""Implementation of Focal Loss from the paper in multiclass classification.
|
|
317
342
|
|
|
318
343
|
Formula:
|
|
319
344
|
loss = - gt * alpha * ((1 - pr)^gamma) * log(pr)
|
|
@@ -325,6 +350,8 @@ def categorical_focal_loss(gt, pr, gamma=2.0, alpha=0.25, class_indexes=None, **
|
|
|
325
350
|
gamma: focusing parameter for modulating factor (1-p), default 2.0
|
|
326
351
|
class_indexes: Optional integer or list of integers, classes to consider, if
|
|
327
352
|
``None`` all classes are used.
|
|
353
|
+
kwargs: additional arguments, for example, ``backend`` is required for some
|
|
354
|
+
operations
|
|
328
355
|
|
|
329
356
|
"""
|
|
330
357
|
gt, pr = gather_channels(gt, pr, indexes=class_indexes, **kwargs)
|
|
@@ -339,7 +366,7 @@ def categorical_focal_loss(gt, pr, gamma=2.0, alpha=0.25, class_indexes=None, **
|
|
|
339
366
|
|
|
340
367
|
|
|
341
368
|
def binary_focal_loss(gt, pr, gamma=2.0, alpha=0.25, **kwargs): # noqa: ARG001
|
|
342
|
-
r"""Implementation of Focal Loss from the paper in binary classification
|
|
369
|
+
r"""Implementation of Focal Loss from the paper in binary classification.
|
|
343
370
|
|
|
344
371
|
Formula:
|
|
345
372
|
loss = - gt * alpha * ((1 - pr)^gamma) * log(pr) \
|
|
@@ -350,6 +377,8 @@ def binary_focal_loss(gt, pr, gamma=2.0, alpha=0.25, **kwargs): # noqa: ARG001
|
|
|
350
377
|
pr: prediction 4D keras tensor (B, H, W, C) or (B, C, H, W)
|
|
351
378
|
alpha: the same as weighting factor in balanced cross entropy, default 0.25
|
|
352
379
|
gamma: focusing parameter for modulating factor (1-p), default 2.0
|
|
380
|
+
kwargs: additional arguments, for example, ``backend`` is required for some
|
|
381
|
+
operations
|
|
353
382
|
|
|
354
383
|
"""
|
|
355
384
|
# clip to prevent NaN's and Inf's
|