dtc_super_resolution 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.
- dtc_super_resolution-0.1.0/LICENSE +14 -0
- dtc_super_resolution-0.1.0/PKG-INFO +61 -0
- dtc_super_resolution-0.1.0/README.md +28 -0
- dtc_super_resolution-0.1.0/pyproject.toml +129 -0
- dtc_super_resolution-0.1.0/src/dtc_super_resolution/__init__.py +1 -0
- dtc_super_resolution-0.1.0/src/dtc_super_resolution/monitoring.py +26 -0
- dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/__init__.py +1 -0
- dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/ml_module/__init__.py +1 -0
- dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/ml_module/attention_unet/__init__.py +1 -0
- dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/ml_module/attention_unet/rcm_emulator_parts.py +372 -0
- dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/ml_module/attention_unet/rcm_emulator_unet.py +104 -0
- dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/ml_module/basic_unet/__init__.py +1 -0
- dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/ml_module/basic_unet/unet.py +88 -0
- dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/ml_module/basic_unet/unet_parts.py +179 -0
- dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/ml_module/datasets.py +83 -0
- dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/ml_module/inference.py +49 -0
- dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/ml_module/inference_helpers.py +301 -0
- dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/ml_module/models_config.yaml +15 -0
- dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/pre_processing_module/__init__.py +1 -0
- dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/pre_processing_module/preprocessing.py +249 -0
- dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/pre_processing_module/reprojection.py +167 -0
- dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/pre_processing_module/tiling.py +589 -0
- dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator_entrypoint.py +51 -0
- dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator_loc_entrypoint.py +56 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Copyright 2026 DTC-Ice-Sheets (DTC-IS) consortium
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
|
4
|
+
documentation files (the “Software”), to deal in the Software without restriction, including without limitation the
|
|
5
|
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
|
|
6
|
+
persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
7
|
+
|
|
8
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
|
9
|
+
Software.
|
|
10
|
+
|
|
11
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
|
12
|
+
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
13
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
14
|
+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dtc_super_resolution
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A DTC-IS module that leverages super-resolution modeling techniques to enhance the monitoring and prediction of surface climate conditions over Greenland and Antarctica.
|
|
5
|
+
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Author: Stef Lhermitte
|
|
8
|
+
Author-email: stef.lhermitte@kuleuven.be
|
|
9
|
+
Requires-Python: >=3.12,<3.15
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
|
+
Requires-Dist: cartopy (>=0.25.0,<0.26.0)
|
|
16
|
+
Requires-Dist: cyclopts (>=4.5.1,<5.0.0)
|
|
17
|
+
Requires-Dist: dotenv (>=0.9.9,<0.10.0)
|
|
18
|
+
Requires-Dist: fsspec[http] (>=2026.9.0,<2027.0.0)
|
|
19
|
+
Requires-Dist: ipykernel (>=7.3.0,<8.0.0)
|
|
20
|
+
Requires-Dist: jupyter (>=1.1.1,<2.0.0)
|
|
21
|
+
Requires-Dist: matplotlib (>=3.11.0,<4.0.0)
|
|
22
|
+
Requires-Dist: numpy (>=2.4,<3.0)
|
|
23
|
+
Requires-Dist: pydantic (>=2.12.5,<3.0.0)
|
|
24
|
+
Requires-Dist: rioxarray (==0.19)
|
|
25
|
+
Requires-Dist: scipy (>=1.18.0,<2.0.0)
|
|
26
|
+
Requires-Dist: seaborn (>=0.13.2,<0.14.0)
|
|
27
|
+
Requires-Dist: torch (==2.10.0)
|
|
28
|
+
Requires-Dist: tqdm (>=4.68.4,<5.0.0)
|
|
29
|
+
Requires-Dist: zarr (>=3.2.1,<4.0.0)
|
|
30
|
+
Project-URL: Homepage, https://github.com/dtc-ice-sheets/dtc_super_resolution
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+

|
|
34
|
+

|
|
35
|
+
[](https://github.com/DTC-Ice-Sheets/dtc_super_resolution/blob/main/LICENSE)
|
|
36
|
+
|
|
37
|
+
# DTC-IS Super Resolution Module
|
|
38
|
+
|
|
39
|
+
The `dtc_super_resolution` module is a sub-component of the
|
|
40
|
+
[Digital Twin Component for Ice Sheets (DTC-IS)](https://dtc-ice-sheets.org/), an ESA-funded, EO-driven digital twin project.
|
|
41
|
+
|
|
42
|
+
This software package leverages super-resolution modeling techniques to enhance the monitoring and prediction of surface climate conditions over Greenland and Antarctica.
|
|
43
|
+
|
|
44
|
+
For an in-depth overview of the dtc_super_resolution module please [read the docs](https://dtc-ice-sheets.github.io/dtc_super_resolution/).
|
|
45
|
+
|
|
46
|
+
The documentation includes sections on:
|
|
47
|
+
|
|
48
|
+
1. [Getting started](https://dtc-ice-sheets.github.io/dtc_super_resolution/quick-start.html)
|
|
49
|
+
1. [Scientific overview](https://dtc-ice-sheets.github.io/dtc_super_resolution/overview.html)
|
|
50
|
+
1. [API reference](https://dtc-ice-sheets.github.io/dtc_super_resolution/api.html)
|
|
51
|
+
|
|
52
|
+
For a more interactive way of exploring the system, please visit the digital twin's [dashboard](https://dashboards.dtc-ice-sheets.org/).
|
|
53
|
+
|
|
54
|
+
## Installation for Use
|
|
55
|
+
|
|
56
|
+
You can install this package with pip:
|
|
57
|
+
|
|
58
|
+
``` bash
|
|
59
|
+
pip install dtc_super_resolution
|
|
60
|
+
```
|
|
61
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+

|
|
2
|
+

|
|
3
|
+
[](https://github.com/DTC-Ice-Sheets/dtc_super_resolution/blob/main/LICENSE)
|
|
4
|
+
|
|
5
|
+
# DTC-IS Super Resolution Module
|
|
6
|
+
|
|
7
|
+
The `dtc_super_resolution` module is a sub-component of the
|
|
8
|
+
[Digital Twin Component for Ice Sheets (DTC-IS)](https://dtc-ice-sheets.org/), an ESA-funded, EO-driven digital twin project.
|
|
9
|
+
|
|
10
|
+
This software package leverages super-resolution modeling techniques to enhance the monitoring and prediction of surface climate conditions over Greenland and Antarctica.
|
|
11
|
+
|
|
12
|
+
For an in-depth overview of the dtc_super_resolution module please [read the docs](https://dtc-ice-sheets.github.io/dtc_super_resolution/).
|
|
13
|
+
|
|
14
|
+
The documentation includes sections on:
|
|
15
|
+
|
|
16
|
+
1. [Getting started](https://dtc-ice-sheets.github.io/dtc_super_resolution/quick-start.html)
|
|
17
|
+
1. [Scientific overview](https://dtc-ice-sheets.github.io/dtc_super_resolution/overview.html)
|
|
18
|
+
1. [API reference](https://dtc-ice-sheets.github.io/dtc_super_resolution/api.html)
|
|
19
|
+
|
|
20
|
+
For a more interactive way of exploring the system, please visit the digital twin's [dashboard](https://dashboards.dtc-ice-sheets.org/).
|
|
21
|
+
|
|
22
|
+
## Installation for Use
|
|
23
|
+
|
|
24
|
+
You can install this package with pip:
|
|
25
|
+
|
|
26
|
+
``` bash
|
|
27
|
+
pip install dtc_super_resolution
|
|
28
|
+
```
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["poetry>=1.5"]
|
|
3
|
+
build-backend = "poetry.core.masonry.api"
|
|
4
|
+
|
|
5
|
+
[tool.poetry]
|
|
6
|
+
name = "dtc_super_resolution"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A DTC-IS module that leverages super-resolution modeling techniques to enhance the monitoring and prediction of surface climate conditions over Greenland and Antarctica."
|
|
9
|
+
authors = [
|
|
10
|
+
"Stef Lhermitte <stef.lhermitte@kuleuven.be>",
|
|
11
|
+
"Earthwave Ltd <info@earthwave.co.uk>",
|
|
12
|
+
]
|
|
13
|
+
license = "MIT"
|
|
14
|
+
readme = "README.md"
|
|
15
|
+
homepage = "https://github.com/dtc-ice-sheets/dtc_super_resolution"
|
|
16
|
+
packages = [{ include = "dtc_super_resolution", from = "src" }]
|
|
17
|
+
|
|
18
|
+
[[tool.poetry.source]]
|
|
19
|
+
name = "pytorch-cpu"
|
|
20
|
+
url = "https://download.pytorch.org/whl/cpu"
|
|
21
|
+
priority = "explicit"
|
|
22
|
+
|
|
23
|
+
[tool.poetry.dependencies]
|
|
24
|
+
python = ">=3.12,<3.15"
|
|
25
|
+
numpy = "^2.4"
|
|
26
|
+
cyclopts = "^4.5.1"
|
|
27
|
+
pydantic = "^2.12.5"
|
|
28
|
+
cartopy = "^0.25.0"
|
|
29
|
+
dotenv = "^0.9.9"
|
|
30
|
+
ipykernel = "^7.3.0"
|
|
31
|
+
jupyter = "^1.1.1"
|
|
32
|
+
matplotlib = "^3.11.0"
|
|
33
|
+
rioxarray = "0.19"
|
|
34
|
+
seaborn = "^0.13.2"
|
|
35
|
+
torch = { version = "2.10.0", source = "pytorch-cpu" }
|
|
36
|
+
tqdm = "^4.68.4"
|
|
37
|
+
zarr = "^3.2.1"
|
|
38
|
+
scipy = "^1.18.0"
|
|
39
|
+
fsspec = { extras = ["http"], version = "^2026.9.0" }
|
|
40
|
+
|
|
41
|
+
[tool.poetry.group.dev.dependencies]
|
|
42
|
+
pytest = "^9.0.2"
|
|
43
|
+
typeguard = "^4.4.4"
|
|
44
|
+
pre-commit = "^4.5"
|
|
45
|
+
ruff = "^0.14.14"
|
|
46
|
+
bandit = "^1.9.3"
|
|
47
|
+
pytest-cov = "^7.0.0"
|
|
48
|
+
sphinx = "^9.1.0"
|
|
49
|
+
sphinx-rtd-theme = "^3.0.2"
|
|
50
|
+
sphinx-copybutton = "^0.5.2"
|
|
51
|
+
commitizen = "^4.11.6"
|
|
52
|
+
pydoclint = "^0.8.3"
|
|
53
|
+
|
|
54
|
+
[tool.poetry.group.deployment.dependencies]
|
|
55
|
+
dtc-wrapper = { git = "https://github.com/DTC-Ice-Sheets/dtc_wrapper.git", tag = "0.0.42" }
|
|
56
|
+
|
|
57
|
+
[tool.ruff]
|
|
58
|
+
line-length = 120
|
|
59
|
+
target-version = "py312"
|
|
60
|
+
|
|
61
|
+
[tool.ruff.lint]
|
|
62
|
+
preview = true
|
|
63
|
+
select = [
|
|
64
|
+
"T20", # Disallow print statements
|
|
65
|
+
"A", # Ambiguity
|
|
66
|
+
"ANN", # Annotations
|
|
67
|
+
"B", # Bad practices
|
|
68
|
+
"C90", # Complexity
|
|
69
|
+
"D", # Documentation
|
|
70
|
+
"E", # Errors
|
|
71
|
+
"F", # Pyflakes (e.g. unused imports and vars)
|
|
72
|
+
"G", # logging formatting
|
|
73
|
+
"I", # Imports
|
|
74
|
+
"N", # Naming
|
|
75
|
+
"Q", # Quoting
|
|
76
|
+
"W", # Warnings
|
|
77
|
+
"PT", # pytest-style
|
|
78
|
+
"SIM", # flake8-simplify
|
|
79
|
+
"C4", # flake8-comprehensions
|
|
80
|
+
"ARG", # flake8-unused-arguments
|
|
81
|
+
"PIE", # flake8-pie
|
|
82
|
+
"RUF", # Ruff-specific rules
|
|
83
|
+
"BLE", # Blind except
|
|
84
|
+
"RET", # Return statements
|
|
85
|
+
"PTH", # Use pathlib
|
|
86
|
+
"PD", # Pandas
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
[tool.pydoclint]
|
|
90
|
+
style = 'numpy'
|
|
91
|
+
check_style_mismatch = "true"
|
|
92
|
+
|
|
93
|
+
mccabe = { max-complexity = 10 }
|
|
94
|
+
|
|
95
|
+
ignore = [
|
|
96
|
+
"D203", # One blank line required before class docstring (conflicts with D211)
|
|
97
|
+
"D204", # One blank line required after class docstring
|
|
98
|
+
"D205", # Docstring with no content
|
|
99
|
+
"D212", # Multi-line docstring summary should start at the second line
|
|
100
|
+
"D213", # Multi-line docstring summary should start at the first line
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
[tool.ruff.lint.per-file-ignores]
|
|
104
|
+
"tests/**" = [
|
|
105
|
+
"T201", # Allow print statements in test files, as they are commonly used for debugging
|
|
106
|
+
"E402", # Allow 'import' statements not being at the top of the file (common in test files for clarity)
|
|
107
|
+
"S101", # Allow the use of 'assert' statements in test files (common practice in pytest)
|
|
108
|
+
"D103", # Ignore missing docstring warnings for functions in test files (commonly not required for small test functions)
|
|
109
|
+
"D102", # Ignore missing docstring warnings for classes in test files
|
|
110
|
+
"D101", # Ignore missing docstring warnings in test files
|
|
111
|
+
"ANN", # Ignore missing type annotations for functions in test files (commonly not required for small test functions)
|
|
112
|
+
]
|
|
113
|
+
"**.ipynb" = [
|
|
114
|
+
"T201", # Allow print statements in Jupyter notebooks, as they are commonly used for debugging and output display
|
|
115
|
+
"E402", # Allow 'import' statements not being at the top of the file (common in notebooks for clarity)
|
|
116
|
+
"S101", # Allow the use of 'assert' statements in notebooks (common practice in exploratory coding)
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
[tool.ruff.lint.pydocstyle]
|
|
120
|
+
convention = "numpy" # Accepts: "google", "numpy", or "pep257"
|
|
121
|
+
[tool.bandit.assert_used]
|
|
122
|
+
skips = ['*_test.py', '*/test_*.py']
|
|
123
|
+
|
|
124
|
+
[tool.pytest.ini_options]
|
|
125
|
+
addopts = "--disable-warnings --cov dtc_super_resolution --cov-fail-under=80"
|
|
126
|
+
typeguard-packages = """dtc_super_resolution"""
|
|
127
|
+
typeguard-debug-instrumentation = true
|
|
128
|
+
typeguard-forward-ref-policy = "ERROR"
|
|
129
|
+
typeguard-collection-check-strategy = "ALL_ITEMS"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Super resolution package."""
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Monitoring and logging tools."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def setup_logging() -> None:
|
|
7
|
+
"""
|
|
8
|
+
Set up root logger.
|
|
9
|
+
|
|
10
|
+
This configuration will be inherited by all module-level loggers created within the package.
|
|
11
|
+
"""
|
|
12
|
+
# Create the root logger
|
|
13
|
+
logger = logging.getLogger()
|
|
14
|
+
logger.setLevel(logging.INFO)
|
|
15
|
+
|
|
16
|
+
# Create a console handler and set the level to info
|
|
17
|
+
console_handler = logging.StreamHandler()
|
|
18
|
+
console_handler.setLevel(logging.INFO)
|
|
19
|
+
|
|
20
|
+
# Create a formatter and set it for the console handler
|
|
21
|
+
formatter = logging.Formatter("[%(asctime)s] %(levelname)s in %(name)s: %(message)s")
|
|
22
|
+
console_handler.setFormatter(formatter)
|
|
23
|
+
|
|
24
|
+
# Add the console handler to the root logger (only if not already added)
|
|
25
|
+
if not logger.handlers:
|
|
26
|
+
logger.addHandler(console_handler)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""RCM emulator package."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Machine learning module."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Attention U-Net model for super-resolution of climate data."""
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
"""Parts of the U-Net model."""
|
|
4
|
+
|
|
5
|
+
# Base model taken from: https://github.com/milesial/Pytorch-UNet
|
|
6
|
+
import torch
|
|
7
|
+
import torch.nn as nn
|
|
8
|
+
import torch.nn.functional as functional
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DoubleConvDS(nn.Module):
|
|
12
|
+
"""(convolution => [BN] => ReLU) * 2."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, in_channels: int, out_channels: int, mid_channels: int = 0, kernels_per_layer: int = 1) -> None:
|
|
15
|
+
super().__init__()
|
|
16
|
+
if not mid_channels:
|
|
17
|
+
mid_channels = out_channels
|
|
18
|
+
self.double_conv = nn.Sequential(
|
|
19
|
+
DepthwiseSeparableConv(
|
|
20
|
+
in_channels,
|
|
21
|
+
mid_channels,
|
|
22
|
+
kernel_size=3,
|
|
23
|
+
kernels_per_layer=kernels_per_layer,
|
|
24
|
+
padding=1,
|
|
25
|
+
),
|
|
26
|
+
nn.GroupNorm(8, mid_channels),
|
|
27
|
+
nn.ReLU(inplace=True),
|
|
28
|
+
DepthwiseSeparableConv(
|
|
29
|
+
mid_channels,
|
|
30
|
+
out_channels,
|
|
31
|
+
kernel_size=3,
|
|
32
|
+
kernels_per_layer=kernels_per_layer,
|
|
33
|
+
padding=1,
|
|
34
|
+
),
|
|
35
|
+
nn.GroupNorm(8, out_channels),
|
|
36
|
+
nn.ReLU(inplace=True),
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
40
|
+
"""Forward pass for the DoubleDSConv module."""
|
|
41
|
+
return self.double_conv(x)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class UpLastConv(nn.Module):
|
|
45
|
+
"""Upscaling then double conv without concatenating with skip connection."""
|
|
46
|
+
|
|
47
|
+
def __init__(
|
|
48
|
+
self,
|
|
49
|
+
in_channels: int,
|
|
50
|
+
out_channels: int,
|
|
51
|
+
mid_channels: int,
|
|
52
|
+
bilinear: bool = True,
|
|
53
|
+
kernels_per_layer: int = 1,
|
|
54
|
+
) -> None:
|
|
55
|
+
super().__init__()
|
|
56
|
+
|
|
57
|
+
# if bilinear, use the normal convolutions to reduce the number of channels
|
|
58
|
+
if bilinear:
|
|
59
|
+
self.up = nn.Upsample(scale_factor=2, mode="bilinear", align_corners=True)
|
|
60
|
+
# self.conv = DoubleConv(in_channels, out_channels, in_channels // 2)
|
|
61
|
+
self.conv = DoubleConvDS(
|
|
62
|
+
in_channels,
|
|
63
|
+
out_channels,
|
|
64
|
+
in_channels // 2,
|
|
65
|
+
kernels_per_layer=kernels_per_layer,
|
|
66
|
+
)
|
|
67
|
+
else:
|
|
68
|
+
self.up = nn.ConvTranspose2d(in_channels, in_channels // 2, kernel_size=3, stride=2)
|
|
69
|
+
# self.conv = DoubleConv(mid_channels, out_channels)
|
|
70
|
+
self.conv = DoubleConvDS(mid_channels, out_channels, kernels_per_layer=kernels_per_layer)
|
|
71
|
+
|
|
72
|
+
def forward(self, x: torch.Tensor, zsize: int = 64) -> torch.Tensor:
|
|
73
|
+
"""Forward pass for the UpLastConv module."""
|
|
74
|
+
x1 = self.up(x)
|
|
75
|
+
# input is CHW
|
|
76
|
+
diff_y = zsize - x1.size()[2]
|
|
77
|
+
diff_x = zsize - x1.size()[3]
|
|
78
|
+
|
|
79
|
+
x1 = functional.pad(x1, [diff_x // 2, diff_x - diff_x // 2, diff_y // 2, diff_y - diff_y // 2])
|
|
80
|
+
return self.conv(x1)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class DownDS(nn.Module):
|
|
84
|
+
"""Downscaling with maxpool then double conv."""
|
|
85
|
+
|
|
86
|
+
def __init__(self, in_channels: int, out_channels: int, kernels_per_layer: int = 1) -> None:
|
|
87
|
+
super().__init__()
|
|
88
|
+
self.maxpool_conv = nn.Sequential(
|
|
89
|
+
nn.MaxPool2d(2),
|
|
90
|
+
DoubleConvDS(in_channels, out_channels, kernels_per_layer=kernels_per_layer),
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
94
|
+
"""Forward pass for the DownDS module."""
|
|
95
|
+
return self.maxpool_conv(x)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class UpDS(nn.Module):
|
|
99
|
+
"""Upscaling then double conv."""
|
|
100
|
+
|
|
101
|
+
def __init__(self, in_channels: int, out_channels: int, bilinear: bool = True, kernels_per_layer: int = 1) -> None:
|
|
102
|
+
super().__init__()
|
|
103
|
+
|
|
104
|
+
# if bilinear, use the normal convolutions to reduce the number of channels
|
|
105
|
+
if bilinear:
|
|
106
|
+
self.up = nn.Upsample(scale_factor=2, mode="bilinear", align_corners=True)
|
|
107
|
+
self.conv = DoubleConvDS(
|
|
108
|
+
in_channels,
|
|
109
|
+
out_channels,
|
|
110
|
+
in_channels // 2,
|
|
111
|
+
kernels_per_layer=kernels_per_layer,
|
|
112
|
+
)
|
|
113
|
+
else:
|
|
114
|
+
self.up = nn.ConvTranspose2d(in_channels, in_channels // 2, kernel_size=3, stride=2)
|
|
115
|
+
self.conv = DoubleConvDS(in_channels, out_channels, kernels_per_layer=kernels_per_layer)
|
|
116
|
+
|
|
117
|
+
def forward(self, x1: torch.Tensor, x2: torch.Tensor) -> torch.Tensor:
|
|
118
|
+
"""Forward pass for the UpDS module."""
|
|
119
|
+
x1 = self.up(x1)
|
|
120
|
+
# input is CHW
|
|
121
|
+
diff_y = x2.size()[2] - x1.size()[2]
|
|
122
|
+
diff_x = x2.size()[3] - x1.size()[3]
|
|
123
|
+
|
|
124
|
+
x1 = functional.pad(x1, [diff_x // 2, diff_x - diff_x // 2, diff_y // 2, diff_y - diff_y // 2])
|
|
125
|
+
# if you have padding issues, see
|
|
126
|
+
# https://github.com/HaiyongJiang/U-Net-Pytorch-Unstructured-Buggy/commit/0e854509c2cea854e247a9c615f175f76fbb2e3a
|
|
127
|
+
# https://github.com/xiaopeng-liao/Pytorch-UNet/commit/8ebac70e633bac59fc22bb5195e513d5832fb3bd
|
|
128
|
+
x = torch.cat([x2, x1], dim=1)
|
|
129
|
+
return self.conv(x)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class OutConv(nn.Module):
|
|
133
|
+
"""Output convolution."""
|
|
134
|
+
|
|
135
|
+
def __init__(self, in_channels: int, out_channels: int) -> None:
|
|
136
|
+
super().__init__()
|
|
137
|
+
self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=1)
|
|
138
|
+
|
|
139
|
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
140
|
+
"""Forward pass for the OutConv module."""
|
|
141
|
+
return self.conv(x)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class InitialConv(nn.Module):
|
|
145
|
+
"""Initial convolution to adapt spatial dimensions."""
|
|
146
|
+
|
|
147
|
+
def __init__(self, in_channels: int, out_channels: int, size_in: int, size_out: int) -> None:
|
|
148
|
+
super().__init__()
|
|
149
|
+
self.n_channels_in = in_channels
|
|
150
|
+
self.n_channels_out = out_channels
|
|
151
|
+
if size_in >= size_out:
|
|
152
|
+
# downsampling via valid convolution
|
|
153
|
+
diff = size_in - size_out + 1
|
|
154
|
+
self.adapt = nn.Sequential(
|
|
155
|
+
nn.Conv2d(in_channels, out_channels, kernel_size=diff),
|
|
156
|
+
nn.BatchNorm2d(out_channels),
|
|
157
|
+
nn.ReLU(inplace=True),
|
|
158
|
+
)
|
|
159
|
+
else:
|
|
160
|
+
# upsampling via bilinear resize + 3x3 conv
|
|
161
|
+
self.adapt = nn.Sequential(
|
|
162
|
+
nn.Upsample(size=size_out, mode="bilinear", align_corners=True),
|
|
163
|
+
nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1),
|
|
164
|
+
nn.BatchNorm2d(out_channels),
|
|
165
|
+
nn.ReLU(inplace=True),
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
169
|
+
"""Forward pass for the InitialConv module."""
|
|
170
|
+
return self.adapt(x)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
# class InitialConv(nn.Module):
|
|
174
|
+
# def __init__(self, in_channels, out_channels, sizex, size):
|
|
175
|
+
# super().__init__()
|
|
176
|
+
# self.diff = sizex - size + 1
|
|
177
|
+
# self.n_channels_in = in_channels
|
|
178
|
+
# self.n_channels_out = out_channels
|
|
179
|
+
# self.firstconv = nn.Sequential(
|
|
180
|
+
# nn.Conv2d(self.n_channels_in, self.n_channels_out, kernel_size=self.diff),
|
|
181
|
+
# nn.BatchNorm2d(out_channels),
|
|
182
|
+
# nn.ReLU(inplace=True),
|
|
183
|
+
# )
|
|
184
|
+
|
|
185
|
+
# def forward(self, x):
|
|
186
|
+
# x1 = self.firstconv(x)
|
|
187
|
+
# return x1
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
# Taken from https://discuss.pytorch.org/t/is-there-any-layer-like-tensorflows-space-to-depth-function/3487/14
|
|
191
|
+
class DepthToSpace(nn.Module):
|
|
192
|
+
"""Depth to space operation."""
|
|
193
|
+
|
|
194
|
+
def __init__(self, block_size: int) -> None:
|
|
195
|
+
super().__init__()
|
|
196
|
+
self.bs = block_size
|
|
197
|
+
|
|
198
|
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
199
|
+
"""Forward pass for the DepthToSpace module."""
|
|
200
|
+
n, c, h, w = x.size()
|
|
201
|
+
x = x.view(n, self.bs, self.bs, c // (self.bs**2), h, w) # (n, bs, bs, c//bs^2, h, w)
|
|
202
|
+
x = x.permute(0, 3, 4, 1, 5, 2).contiguous() # (n, c//bs^2, h, bs, w, bs)
|
|
203
|
+
return x.view(n, c // (self.bs**2), h * self.bs, w * self.bs) # (n, c//bs^2, h * bs, w * bs)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class SpaceToDepth(nn.Module):
|
|
207
|
+
"""Space to depth operation."""
|
|
208
|
+
|
|
209
|
+
# Expects the following shape: Batch, Channel, Height, Width
|
|
210
|
+
def __init__(self, block_size: int) -> None:
|
|
211
|
+
super().__init__()
|
|
212
|
+
self.bs = block_size
|
|
213
|
+
|
|
214
|
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
215
|
+
"""Forward pass for the SpaceToDepth module."""
|
|
216
|
+
n, c, h, w = x.size()
|
|
217
|
+
x = x.view(n, c, h // self.bs, self.bs, w // self.bs, self.bs) # (n, c, h//bs, bs, w//bs, bs)
|
|
218
|
+
x = x.permute(0, 3, 5, 1, 2, 4).contiguous() # (n, bs, bs, c, h//bs, w//bs)
|
|
219
|
+
return x.view(n, c * (self.bs**2), h // self.bs, w // self.bs) # (n, c*bs^2, h//bs, w//bs)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class DepthwiseSeparableConv(nn.Module):
|
|
223
|
+
"""Depthwise separable convolution."""
|
|
224
|
+
|
|
225
|
+
def __init__(
|
|
226
|
+
self, in_channels: int, output_channels: int, kernel_size: int, padding: int = 0, kernels_per_layer: int = 1
|
|
227
|
+
) -> None:
|
|
228
|
+
super().__init__()
|
|
229
|
+
# In Tensorflow DepthwiseConv2D has depth_multiplier instead of kernels_per_layer
|
|
230
|
+
self.depthwise = nn.Conv2d(
|
|
231
|
+
in_channels,
|
|
232
|
+
in_channels * kernels_per_layer,
|
|
233
|
+
kernel_size=kernel_size,
|
|
234
|
+
padding=padding,
|
|
235
|
+
groups=in_channels,
|
|
236
|
+
)
|
|
237
|
+
self.pointwise = nn.Conv2d(in_channels * kernels_per_layer, output_channels, kernel_size=1)
|
|
238
|
+
|
|
239
|
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
240
|
+
"""Forward pass for the DepthwiseSeparableConv module."""
|
|
241
|
+
x = self.depthwise(x)
|
|
242
|
+
return self.pointwise(x)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
class DoubleDense(nn.Module):
|
|
246
|
+
"""(dense => [ReLU]) * 2."""
|
|
247
|
+
|
|
248
|
+
def __init__(self, in_channels: int, hidden_neurons: int, output_channels: int) -> None:
|
|
249
|
+
super().__init__()
|
|
250
|
+
self.dense1 = nn.Linear(in_channels, out_features=hidden_neurons)
|
|
251
|
+
self.dense2 = nn.Linear(in_features=hidden_neurons, out_features=hidden_neurons // 2)
|
|
252
|
+
self.dense3 = nn.Linear(in_features=hidden_neurons // 2, out_features=output_channels)
|
|
253
|
+
|
|
254
|
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
255
|
+
"""Forward pass for the DoubleDense module."""
|
|
256
|
+
out = functional.relu(self.dense1(x.view(x.size(0), -1)))
|
|
257
|
+
out = functional.relu(self.dense2(out))
|
|
258
|
+
return self.dense3(out)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class DoubleDSConv(nn.Module):
|
|
262
|
+
"""(convolution => [BN] => ReLU) * 2."""
|
|
263
|
+
|
|
264
|
+
def __init__(self, in_channels: int, out_channels: int) -> None:
|
|
265
|
+
super().__init__()
|
|
266
|
+
self.double_ds_conv = nn.Sequential(
|
|
267
|
+
DepthwiseSeparableConv(in_channels, out_channels, kernel_size=3, padding=1),
|
|
268
|
+
nn.BatchNorm2d(out_channels),
|
|
269
|
+
nn.ReLU(inplace=True),
|
|
270
|
+
DepthwiseSeparableConv(out_channels, out_channels, kernel_size=3, padding=1),
|
|
271
|
+
nn.BatchNorm2d(out_channels),
|
|
272
|
+
nn.ReLU(inplace=True),
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
276
|
+
"""Forward pass for the DoubleDSConv module."""
|
|
277
|
+
return self.double_ds_conv(x)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
class Flatten(nn.Module):
|
|
281
|
+
"""Flatten module to reshape the input tensor."""
|
|
282
|
+
|
|
283
|
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
284
|
+
"""Forward pass for the Flatten module."""
|
|
285
|
+
return x.view(x.size(0), -1)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
class ChannelAttention(nn.Module):
|
|
289
|
+
"""Channel attention module."""
|
|
290
|
+
|
|
291
|
+
def __init__(self, input_channels: int, reduction_ratio: int = 16) -> None:
|
|
292
|
+
super().__init__()
|
|
293
|
+
self.input_channels = input_channels
|
|
294
|
+
self.avg_pool = nn.AdaptiveAvgPool2d(1)
|
|
295
|
+
self.max_pool = nn.AdaptiveMaxPool2d(1)
|
|
296
|
+
# https://github.com/luuuyi/CBAM.PyTorch/blob/master/model/resnet_cbam.py
|
|
297
|
+
# uses Convolutions instead of Linear
|
|
298
|
+
self.MLP = nn.Sequential(
|
|
299
|
+
Flatten(),
|
|
300
|
+
nn.Linear(input_channels, input_channels // reduction_ratio),
|
|
301
|
+
nn.ReLU(),
|
|
302
|
+
nn.Linear(input_channels // reduction_ratio, input_channels),
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
306
|
+
"""Forward pass for the ChannelAttention module."""
|
|
307
|
+
# Take the input and apply average and max pooling
|
|
308
|
+
avg_values = self.avg_pool(x)
|
|
309
|
+
max_values = self.max_pool(x)
|
|
310
|
+
out = self.MLP(avg_values) + self.MLP(max_values)
|
|
311
|
+
return x * torch.sigmoid(out).unsqueeze(2).unsqueeze(3).expand_as(x)
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
class SpatialAttention(nn.Module):
|
|
315
|
+
"""Spatial attention module."""
|
|
316
|
+
|
|
317
|
+
def __init__(self, kernel_size: int = 7) -> None:
|
|
318
|
+
super().__init__()
|
|
319
|
+
assert kernel_size in (3, 7), "kernel size must be 3 or 7"
|
|
320
|
+
padding = 3 if kernel_size == 7 else 1
|
|
321
|
+
self.conv = nn.Conv2d(2, 1, kernel_size=kernel_size, padding=padding, bias=False)
|
|
322
|
+
self.bn = nn.Identity()
|
|
323
|
+
|
|
324
|
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
325
|
+
"""Forward pass for the SpatialAttention module."""
|
|
326
|
+
avg_out = torch.mean(x, dim=1, keepdim=True)
|
|
327
|
+
max_out, _ = torch.max(x, dim=1, keepdim=True)
|
|
328
|
+
out = torch.cat([avg_out, max_out], dim=1)
|
|
329
|
+
out = self.conv(out)
|
|
330
|
+
out = self.bn(out)
|
|
331
|
+
return x * torch.sigmoid(out)
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
class CBAM(nn.Module):
|
|
335
|
+
"""Convolutional Block Attention Module (CBAM)."""
|
|
336
|
+
|
|
337
|
+
def __init__(self, input_channels: int, reduction_ratio: int = 16, kernel_size: int = 7) -> None:
|
|
338
|
+
super().__init__()
|
|
339
|
+
self.channel_att = ChannelAttention(input_channels, reduction_ratio=reduction_ratio)
|
|
340
|
+
self.spatial_att = SpatialAttention(kernel_size=kernel_size)
|
|
341
|
+
|
|
342
|
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
343
|
+
"""Forward pass for the CBAM module."""
|
|
344
|
+
out = self.channel_att(x)
|
|
345
|
+
return self.spatial_att(out)
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
class LinearUpsamplingZ(nn.Module):
|
|
349
|
+
"""Linear upsampling of z to match the number of channels of downsampled x."""
|
|
350
|
+
|
|
351
|
+
# Linear upsampling of z to same number of channels as downsampled X
|
|
352
|
+
# so that can concatenate
|
|
353
|
+
def __init__(self, in_channels: int = 16, out_channels: int = 0) -> None:
|
|
354
|
+
super().__init__()
|
|
355
|
+
|
|
356
|
+
if out_channels == 0:
|
|
357
|
+
out_channels = 1024
|
|
358
|
+
|
|
359
|
+
self.linearUp = nn.Sequential(
|
|
360
|
+
nn.Linear(in_channels, 128),
|
|
361
|
+
nn.Linear(128, 256),
|
|
362
|
+
nn.Linear(256, 512),
|
|
363
|
+
nn.Linear(512, out_channels),
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
def forward(self, z: torch.Tensor) -> torch.Tensor:
|
|
367
|
+
"""Forward pass for linear upsampling of z."""
|
|
368
|
+
# transform to remove last dimensions
|
|
369
|
+
z1 = z.view(z.shape[0], -1) # (batch, 16)
|
|
370
|
+
z5 = self.linearUp(z1.float()) # (batch, 1024)
|
|
371
|
+
# transform back to original shape
|
|
372
|
+
return z5.view(z5.shape[0], 1, 1, -1).transpose(3, 1) # (batch, 1024, 1, 1)
|