segmodels-keras 0.1.0__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/LICENSE +21 -0
- segmodels_keras-0.1.0/MANIFEST.in +1 -0
- segmodels_keras-0.1.0/PKG-INFO +36 -0
- segmodels_keras-0.1.0/README.rst +58 -0
- segmodels_keras-0.1.0/pyproject.toml +68 -0
- segmodels_keras-0.1.0/segmodels_keras/__init__.py +141 -0
- segmodels_keras-0.1.0/segmodels_keras/_compat.py +6 -0
- segmodels_keras-0.1.0/segmodels_keras/_version.py +3 -0
- segmodels_keras-0.1.0/segmodels_keras/backbones/__init__.py +1 -0
- segmodels_keras-0.1.0/segmodels_keras/backbones/_common_blocks.py +134 -0
- segmodels_keras-0.1.0/segmodels_keras/backbones/_weights.py +365 -0
- segmodels_keras-0.1.0/segmodels_keras/backbones/backbones_factory.py +423 -0
- segmodels_keras-0.1.0/segmodels_keras/backbones/inception_resnet_v2.py +452 -0
- segmodels_keras-0.1.0/segmodels_keras/backbones/inception_v3.py +504 -0
- segmodels_keras-0.1.0/segmodels_keras/backbones/resnet_18_34.py +367 -0
- segmodels_keras-0.1.0/segmodels_keras/backbones/resnet_common.py +122 -0
- segmodels_keras-0.1.0/segmodels_keras/base/__init__.py +6 -0
- segmodels_keras-0.1.0/segmodels_keras/base/functional.py +390 -0
- segmodels_keras-0.1.0/segmodels_keras/base/objects.py +129 -0
- segmodels_keras-0.1.0/segmodels_keras/losses.py +295 -0
- segmodels_keras-0.1.0/segmodels_keras/metrics.py +286 -0
- segmodels_keras-0.1.0/segmodels_keras/models/__init__.py +5 -0
- segmodels_keras-0.1.0/segmodels_keras/models/_common_blocks.py +67 -0
- segmodels_keras-0.1.0/segmodels_keras/models/_utils.py +14 -0
- segmodels_keras-0.1.0/segmodels_keras/models/fpn.py +313 -0
- segmodels_keras-0.1.0/segmodels_keras/models/linknet.py +330 -0
- segmodels_keras-0.1.0/segmodels_keras/models/model_factory.py +96 -0
- segmodels_keras-0.1.0/segmodels_keras/models/pspnet.py +298 -0
- segmodels_keras-0.1.0/segmodels_keras/models/unet.py +297 -0
- segmodels_keras-0.1.0/segmodels_keras/py.typed +0 -0
- segmodels_keras-0.1.0/segmodels_keras/utils.py +314 -0
- segmodels_keras-0.1.0/segmodels_keras.egg-info/PKG-INFO +36 -0
- segmodels_keras-0.1.0/segmodels_keras.egg-info/SOURCES.txt +41 -0
- segmodels_keras-0.1.0/segmodels_keras.egg-info/dependency_links.txt +1 -0
- segmodels_keras-0.1.0/segmodels_keras.egg-info/requires.txt +7 -0
- segmodels_keras-0.1.0/segmodels_keras.egg-info/top_level.txt +1 -0
- segmodels_keras-0.1.0/setup.cfg +4 -0
- segmodels_keras-0.1.0/setup.py +84 -0
- segmodels_keras-0.1.0/tests/test_backbones.py +23 -0
- segmodels_keras-0.1.0/tests/test_helper.py +5 -0
- segmodels_keras-0.1.0/tests/test_metrics.py +207 -0
- segmodels_keras-0.1.0/tests/test_models.py +128 -0
- segmodels_keras-0.1.0/tests/test_utils.py +228 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018, Pavel Yakubovskiy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
include README.md LICENSE requirements.txt
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: segmodels_keras
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Image segmentation models with pre-trained backbones with Keras.
|
|
5
|
+
Home-page: https://github.com/orthoseg/segmodels_keras
|
|
6
|
+
Author: Pieter Roggemans
|
|
7
|
+
Author-email: pieter.roggemans@gmail.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
13
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
14
|
+
Requires-Python: >=3.0.0
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: keras
|
|
18
|
+
Requires-Dist: packaging
|
|
19
|
+
Provides-Extra: tests
|
|
20
|
+
Requires-Dist: numpy; extra == "tests"
|
|
21
|
+
Requires-Dist: pytest; extra == "tests"
|
|
22
|
+
Requires-Dist: scikit-image; extra == "tests"
|
|
23
|
+
Dynamic: author
|
|
24
|
+
Dynamic: author-email
|
|
25
|
+
Dynamic: classifier
|
|
26
|
+
Dynamic: description
|
|
27
|
+
Dynamic: description-content-type
|
|
28
|
+
Dynamic: home-page
|
|
29
|
+
Dynamic: license
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
Dynamic: provides-extra
|
|
32
|
+
Dynamic: requires-dist
|
|
33
|
+
Dynamic: requires-python
|
|
34
|
+
Dynamic: summary
|
|
35
|
+
|
|
36
|
+
Image segmentation models with pre-trained backbones with Keras.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
|Actions Status| |Code coverage| |PyPI version|
|
|
2
|
+
|
|
3
|
+
.. |Actions Status| image:: https://github.com/orthoseg/segmodels_keras/actions/workflows/tests.yml/badge.svg?branch=main
|
|
4
|
+
:target: https://github.com/orthoseg/segmodels_keras/actions/workflows/tests.yml?query=workflow%3ATests
|
|
5
|
+
|
|
6
|
+
.. |Code coverage| image:: https://codecov.io/gh/orthoseg/segmodels_keras/graph/badge.svg?token=o01OyTMDuj
|
|
7
|
+
:target: https://codecov.io/gh/orthoseg/segmodels_keras
|
|
8
|
+
|
|
9
|
+
.. |PyPI version| image:: https://img.shields.io/pypi/v/segmodels-keras.svg
|
|
10
|
+
:target: https://pypi.org/project/segmodels-keras
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
###############
|
|
14
|
+
segmodels_keras
|
|
15
|
+
###############
|
|
16
|
+
|
|
17
|
+
This is a fork of the
|
|
18
|
+
`segmentation_models <https://github.com/qubvel/segmentation_models>`__ library by
|
|
19
|
+
Pavel Iakubovskii, which is not maintained anymore.
|
|
20
|
+
|
|
21
|
+
This fork is updated to support Keras 3, and also contains some bug fixes, some
|
|
22
|
+
improvements and support for some extra backbone models.
|
|
23
|
+
|
|
24
|
+
It is not meant as a full replacement of the original library, but rather as a
|
|
25
|
+
solution for a library I developed and depended on segmentation_models:
|
|
26
|
+
`orthoseg <https://github.com/orthoseg/orthoseg>`__ . Hence, full backwards
|
|
27
|
+
compatibility or support for all features is not an explicit goal. For example,
|
|
28
|
+
the resnet backbones got small changes compared to the original library, so existing
|
|
29
|
+
weights you saved for them won't be compatible.
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
**The main features** of this library are:
|
|
33
|
+
|
|
34
|
+
- High level API (just two lines of code to create model for segmentation)
|
|
35
|
+
- **4** models architectures for binary and multi-class image segmentation
|
|
36
|
+
(including legendary **Unet**)
|
|
37
|
+
- **20+** available backbones for each architecture
|
|
38
|
+
- All backbones have **pre-trained** weights for faster and better convergence
|
|
39
|
+
- Helpful segmentation losses (Jaccard, Dice, Focal) and metrics (IoU, F-score)
|
|
40
|
+
|
|
41
|
+
Documentation
|
|
42
|
+
~~~~~~~~~~~~~
|
|
43
|
+
The **documentation** of the current stable version is available here:
|
|
44
|
+
`Read the docs, stable <https://segmodels_keras.readthedocs.io/en/stable/>`__.
|
|
45
|
+
|
|
46
|
+
The lastest version of the documentation (= of the main branch) can be found here:
|
|
47
|
+
`Read the docs, latest <https://segmodels_keras.readthedocs.io/en/latest/>`__.
|
|
48
|
+
|
|
49
|
+
Change Log
|
|
50
|
+
~~~~~~~~~~
|
|
51
|
+
To see important changes between versions look at CHANGELOG.md_
|
|
52
|
+
|
|
53
|
+
License
|
|
54
|
+
~~~~~~~
|
|
55
|
+
Project is distributed under `MIT Licence`_.
|
|
56
|
+
|
|
57
|
+
.. _CHANGELOG.md: https://github.com/orthoseg/segmodels_keras/blob/main/CHANGELOG.md
|
|
58
|
+
.. _`MIT Licence`: https://github.com/orthoseg/segmodels_keras/blob/main/LICENSE
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
[[tool.mypy.overrides]]
|
|
2
|
+
module = "h5py.*,keras.*,six.*,setuptools.*,sphinx_rtd_theme.*,tensorflow.*"
|
|
3
|
+
ignore_missing_imports = true
|
|
4
|
+
|
|
5
|
+
[tool.pyright]
|
|
6
|
+
exclude = ["local_ignore", "**/node_modules", "**/__pycache__", "**/.*"]
|
|
7
|
+
ignore = ["*"]
|
|
8
|
+
|
|
9
|
+
[tool.pytest.ini_options]
|
|
10
|
+
log_level = "DEBUG"
|
|
11
|
+
# Write all logging and warnings immediately while running test with debugger.
|
|
12
|
+
# log_cli = true
|
|
13
|
+
# addopts = "-p no:warnings"
|
|
14
|
+
|
|
15
|
+
[tool.ruff]
|
|
16
|
+
line-length = 88
|
|
17
|
+
target-version = "py310"
|
|
18
|
+
extend-exclude = ["docs/conf.py", "local_ignore/*", "examples"]
|
|
19
|
+
|
|
20
|
+
[tool.ruff.lint]
|
|
21
|
+
select = [
|
|
22
|
+
"YTT", # flake8-2020
|
|
23
|
+
# "ANN", # flake8-annotations
|
|
24
|
+
"B", # flake8-bugbear
|
|
25
|
+
"A", # flake8-builtins
|
|
26
|
+
"C4", # flake8-comprehensions
|
|
27
|
+
"T10", # flake8-debugger
|
|
28
|
+
"ISC", # flake8-implicit string concatenation
|
|
29
|
+
# "G", # flake8-logging-format
|
|
30
|
+
"PIE", # flake8-pie misc lints
|
|
31
|
+
# "SIM", # flake8-simplify
|
|
32
|
+
"TC", # flake8-type-checking imports
|
|
33
|
+
"ARG", # flake8-unused-arguments
|
|
34
|
+
# "PTH", # flake8-use-pathlib
|
|
35
|
+
"FLY", # flynt
|
|
36
|
+
"I", # isort
|
|
37
|
+
"NPY", # NumPy-specific rules
|
|
38
|
+
"E", # pycodestyle errors
|
|
39
|
+
"W", # pycodestyle warnings
|
|
40
|
+
"D", # pydocstyle
|
|
41
|
+
"F", # pyflakes
|
|
42
|
+
"PLC", # pylint convention
|
|
43
|
+
"PLE", # pylint error
|
|
44
|
+
"PLR", # pylint refactor
|
|
45
|
+
"PLW", # pylint warning
|
|
46
|
+
"UP", # pyupgrade
|
|
47
|
+
"RUF", # Ruff-specific rules
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
ignore = [
|
|
51
|
+
### Intentionally disabled
|
|
52
|
+
"E402", # Module level import not at top of file
|
|
53
|
+
"PLR0913", # Too many arguments to function call
|
|
54
|
+
"PLR0917", # Too many positional arguments
|
|
55
|
+
"PLR0911", # Too many returns
|
|
56
|
+
"PLR0912", # Too many branches
|
|
57
|
+
"PLR0915", # Too many statements
|
|
58
|
+
"PLR2004", # Magic number
|
|
59
|
+
"PLR5501", # Use `elif` instead of `else` then `if`, to reduce indentation
|
|
60
|
+
"PLW2901", # Loop variable overwritten by assignment target
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
[tool.ruff.lint.per-file-ignores]
|
|
64
|
+
"tests/*" = ["ANN", "D"]
|
|
65
|
+
"perftests/*" = ["ANN", "D"]
|
|
66
|
+
|
|
67
|
+
[tool.ruff.lint.pydocstyle]
|
|
68
|
+
convention = "google"
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""Segmentation Models for Keras."""
|
|
2
|
+
|
|
3
|
+
import functools # noqa: I001
|
|
4
|
+
import os
|
|
5
|
+
from typing import Any
|
|
6
|
+
from collections.abc import Callable
|
|
7
|
+
|
|
8
|
+
# __version__ should be defined as soon as possible
|
|
9
|
+
from ._version import __version__
|
|
10
|
+
|
|
11
|
+
from . import base
|
|
12
|
+
|
|
13
|
+
_KERAS_FRAMEWORK_NAME: str = "keras"
|
|
14
|
+
_TF_KERAS_FRAMEWORK_NAME: str = "tf.keras"
|
|
15
|
+
|
|
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
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def inject_global_losses(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
26
|
+
@functools.wraps(func)
|
|
27
|
+
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
28
|
+
kwargs["losses"] = _KERAS_LOSSES
|
|
29
|
+
return func(*args, **kwargs)
|
|
30
|
+
|
|
31
|
+
return wrapper
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def filter_kwargs(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
35
|
+
@functools.wraps(func)
|
|
36
|
+
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
37
|
+
new_kwargs = {
|
|
38
|
+
k: v
|
|
39
|
+
for k, v in kwargs.items()
|
|
40
|
+
if k in ["backend", "layers", "models", "utils"]
|
|
41
|
+
}
|
|
42
|
+
return func(*args, **new_kwargs)
|
|
43
|
+
|
|
44
|
+
return wrapper
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def framework() -> str | None:
|
|
48
|
+
"""Return name of Segmentation Models framework."""
|
|
49
|
+
return _KERAS_FRAMEWORK
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def set_framework(name: str) -> None:
|
|
53
|
+
"""Set framework for Segmentation Models.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
name (str): one of ``keras``, ``tf.keras``, case insensitive.
|
|
57
|
+
|
|
58
|
+
Raises:
|
|
59
|
+
ValueError: in case of incorrect framework name.
|
|
60
|
+
ImportError: in case framework is not installed.
|
|
61
|
+
|
|
62
|
+
"""
|
|
63
|
+
name = name.lower()
|
|
64
|
+
|
|
65
|
+
if name == _KERAS_FRAMEWORK_NAME:
|
|
66
|
+
import keras # noqa: PLC0415
|
|
67
|
+
elif name == _TF_KERAS_FRAMEWORK_NAME:
|
|
68
|
+
from tensorflow import keras # noqa: PLC0415
|
|
69
|
+
else:
|
|
70
|
+
raise ValueError(
|
|
71
|
+
f"Not correct module name `{name}`, use `{_KERAS_FRAMEWORK_NAME}` or "
|
|
72
|
+
f"`{_TF_KERAS_FRAMEWORK_NAME}`"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
global _KERAS_BACKEND, _KERAS_LAYERS, _KERAS_MODELS # noqa: PLW0603
|
|
76
|
+
global _KERAS_UTILS, _KERAS_LOSSES, _KERAS_FRAMEWORK # noqa: PLW0603
|
|
77
|
+
|
|
78
|
+
_KERAS_FRAMEWORK = name
|
|
79
|
+
_KERAS_BACKEND = keras.backend
|
|
80
|
+
_KERAS_LAYERS = keras.layers
|
|
81
|
+
_KERAS_MODELS = keras.models
|
|
82
|
+
_KERAS_UTILS = keras.utils
|
|
83
|
+
_KERAS_LOSSES = keras.losses
|
|
84
|
+
|
|
85
|
+
# allow losses/metrics get keras submodules
|
|
86
|
+
base.KerasObject.set_submodules(
|
|
87
|
+
backend=keras.backend,
|
|
88
|
+
layers=keras.layers,
|
|
89
|
+
models=keras.models,
|
|
90
|
+
utils=keras.utils,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# set default framework
|
|
95
|
+
_framework = os.environ.get("SM_FRAMEWORK", _DEFAULT_KERAS_FRAMEWORK)
|
|
96
|
+
try:
|
|
97
|
+
set_framework(_framework)
|
|
98
|
+
except ImportError:
|
|
99
|
+
other = (
|
|
100
|
+
_TF_KERAS_FRAMEWORK_NAME
|
|
101
|
+
if _framework == _KERAS_FRAMEWORK_NAME
|
|
102
|
+
else _KERAS_FRAMEWORK_NAME
|
|
103
|
+
)
|
|
104
|
+
set_framework(other)
|
|
105
|
+
|
|
106
|
+
print(f"Segmentation Models: using `{_KERAS_FRAMEWORK}` framework.")
|
|
107
|
+
|
|
108
|
+
# import helper modules
|
|
109
|
+
from . import losses, metrics, utils
|
|
110
|
+
|
|
111
|
+
# wrap segmentation models with framework modules
|
|
112
|
+
from .backbones.backbones_factory import Backbones
|
|
113
|
+
from .models.fpn import FPN
|
|
114
|
+
from .models.linknet import Linknet
|
|
115
|
+
from .models.model_factory import get_model
|
|
116
|
+
from .models.pspnet import PSPNet
|
|
117
|
+
from .models.unet import Unet
|
|
118
|
+
|
|
119
|
+
get_available_backbone_names = Backbones.models_names
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def get_preprocessing(name: str) -> Callable[[Any], Any]:
|
|
123
|
+
"""Get the preprocessing function for a backbone by name."""
|
|
124
|
+
return Backbones.get_preprocessing(name)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
__all__ = [
|
|
128
|
+
"FPN",
|
|
129
|
+
"Linknet",
|
|
130
|
+
"PSPNet",
|
|
131
|
+
"Unet",
|
|
132
|
+
"__version__",
|
|
133
|
+
"framework",
|
|
134
|
+
"get_available_backbone_names",
|
|
135
|
+
"get_model",
|
|
136
|
+
"get_preprocessing",
|
|
137
|
+
"losses",
|
|
138
|
+
"metrics",
|
|
139
|
+
"set_framework",
|
|
140
|
+
"utils",
|
|
141
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Backbones package."""
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""Common blocks for backbone models.
|
|
2
|
+
|
|
3
|
+
Source: https://github.com/qubvel/classification_models
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from keras import backend, layers
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def slice_tensor(x, start, stop, axis):
|
|
10
|
+
if axis == 3:
|
|
11
|
+
return x[:, :, :, start:stop]
|
|
12
|
+
elif axis == 1:
|
|
13
|
+
return x[:, start:stop, :, :]
|
|
14
|
+
else:
|
|
15
|
+
raise ValueError(f"Slice axis should be in (1, 3), got {axis}.")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def GroupConv2D(
|
|
19
|
+
filters,
|
|
20
|
+
kernel_size,
|
|
21
|
+
strides=(1, 1),
|
|
22
|
+
groups=32,
|
|
23
|
+
kernel_initializer="he_uniform",
|
|
24
|
+
use_bias=True,
|
|
25
|
+
activation="linear",
|
|
26
|
+
padding="valid",
|
|
27
|
+
**kwargs, # noqa: ARG001
|
|
28
|
+
):
|
|
29
|
+
"""Grouped Convolution Layer implemented as a Slice, Conv2D and Concatenate layers.
|
|
30
|
+
|
|
31
|
+
Split filters to groups, apply Conv2D and concatenate back.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
filters: Integer, the dimensionality of the output space
|
|
35
|
+
(i.e. the number of output filters in the convolution).
|
|
36
|
+
kernel_size: An integer or tuple/list of a single integer,
|
|
37
|
+
specifying the length of the 1D convolution window.
|
|
38
|
+
strides: An integer or tuple/list of a single integer, specifying the stride
|
|
39
|
+
length of the convolution.
|
|
40
|
+
groups: Integer, number of groups to split input filters to.
|
|
41
|
+
kernel_initializer: Regularizer function applied to the kernel weights matrix.
|
|
42
|
+
use_bias: Boolean, whether the layer uses a bias vector.
|
|
43
|
+
activation: Activation function to use (see activations).
|
|
44
|
+
If you don't specify anything, no activation is applied
|
|
45
|
+
(ie. "linear" activation: a(x) = x).
|
|
46
|
+
padding: one of "valid" or "same" (case-insensitive).
|
|
47
|
+
kwargs: additional keyword arguments (unused).
|
|
48
|
+
|
|
49
|
+
Input shape:
|
|
50
|
+
4D tensor with shape: (batch, rows, cols, channels) if data_format is
|
|
51
|
+
"channels_last".
|
|
52
|
+
|
|
53
|
+
Output shape:
|
|
54
|
+
4D tensor with shape: (batch, new_rows, new_cols, filters) if data_format is
|
|
55
|
+
"channels_last". rows and cols values might have changed due to padding.
|
|
56
|
+
|
|
57
|
+
"""
|
|
58
|
+
slice_axis = 3 if backend.image_data_format() == "channels_last" else 1
|
|
59
|
+
|
|
60
|
+
def layer(input_tensor):
|
|
61
|
+
inp_ch = int(
|
|
62
|
+
backend.int_shape(input_tensor)[-1] // groups
|
|
63
|
+
) # input grouped channels
|
|
64
|
+
out_ch = int(filters // groups) # output grouped channels
|
|
65
|
+
|
|
66
|
+
blocks = []
|
|
67
|
+
for c in range(groups):
|
|
68
|
+
slice_arguments = {
|
|
69
|
+
"start": c * inp_ch,
|
|
70
|
+
"stop": (c + 1) * inp_ch,
|
|
71
|
+
"axis": slice_axis,
|
|
72
|
+
}
|
|
73
|
+
x = layers.Lambda(slice_tensor, arguments=slice_arguments)(input_tensor)
|
|
74
|
+
x = layers.Conv2D(
|
|
75
|
+
out_ch,
|
|
76
|
+
kernel_size,
|
|
77
|
+
strides=strides,
|
|
78
|
+
kernel_initializer=kernel_initializer,
|
|
79
|
+
use_bias=use_bias,
|
|
80
|
+
activation=activation,
|
|
81
|
+
padding=padding,
|
|
82
|
+
)(x)
|
|
83
|
+
blocks.append(x)
|
|
84
|
+
|
|
85
|
+
x = layers.Concatenate(axis=slice_axis)(blocks)
|
|
86
|
+
return x
|
|
87
|
+
|
|
88
|
+
return layer
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def expand_dims(x, channels_axis):
|
|
92
|
+
if channels_axis == 3:
|
|
93
|
+
return x[:, None, None, :]
|
|
94
|
+
elif channels_axis == 1:
|
|
95
|
+
return x[:, :, None, None]
|
|
96
|
+
else:
|
|
97
|
+
raise ValueError(f"Slice axis should be in (1, 3), got {channels_axis}.")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def ChannelSE(reduction=16, **kwargs): # noqa: ARG001
|
|
101
|
+
"""Squeeze and Excitation block.
|
|
102
|
+
|
|
103
|
+
Reimplementation inspired by
|
|
104
|
+
https://github.com/Cadene/pretrained-models.pytorch/blob/master/pretrainedmodels/models/senet.py.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
reduction: channels squeeze factor
|
|
108
|
+
kwargs: additional keyword arguments (unused).
|
|
109
|
+
|
|
110
|
+
"""
|
|
111
|
+
channels_axis = 3 if backend.image_data_format() == "channels_last" else 1
|
|
112
|
+
|
|
113
|
+
def layer(input_tensor):
|
|
114
|
+
# get number of channels/filters
|
|
115
|
+
channels = backend.int_shape(input_tensor)[channels_axis]
|
|
116
|
+
|
|
117
|
+
x = input_tensor
|
|
118
|
+
|
|
119
|
+
# squeeze and excitation block in PyTorch style with
|
|
120
|
+
x = layers.GlobalAveragePooling2D()(x)
|
|
121
|
+
x = layers.Lambda(expand_dims, arguments={"channels_axis": channels_axis})(x)
|
|
122
|
+
x = layers.Conv2D(
|
|
123
|
+
channels // reduction, (1, 1), kernel_initializer="he_uniform"
|
|
124
|
+
)(x)
|
|
125
|
+
x = layers.Activation("relu")(x)
|
|
126
|
+
x = layers.Conv2D(channels, (1, 1), kernel_initializer="he_uniform")(x)
|
|
127
|
+
x = layers.Activation("sigmoid")(x)
|
|
128
|
+
|
|
129
|
+
# apply attention
|
|
130
|
+
x = layers.Multiply()([input_tensor, x])
|
|
131
|
+
|
|
132
|
+
return x
|
|
133
|
+
|
|
134
|
+
return layer
|