Skip to content

Commit 61c738d

Browse files
authored
Merge branch 'dev' into issue-8225-docstring-brats
2 parents 5d2016c + e6cae1c commit 61c738d

30 files changed

+1189
-108
lines changed

.github/workflows/cron.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ jobs:
7575
if pgrep python; then pkill python; fi
7676
shell: bash
7777
- name: Upload coverage
78-
uses: codecov/codecov-action@v4
78+
uses: codecov/codecov-action@v5
7979
with:
8080
fail_ci_if_error: false
8181
files: ./coverage.xml
@@ -123,7 +123,7 @@ jobs:
123123
if pgrep python; then pkill python; fi
124124
shell: bash
125125
- name: Upload coverage
126-
uses: codecov/codecov-action@v4
126+
uses: codecov/codecov-action@v5
127127
with:
128128
fail_ci_if_error: false
129129
files: ./coverage.xml
@@ -228,7 +228,7 @@ jobs:
228228
if pgrep python; then pkill python; fi
229229
shell: bash
230230
- name: Upload coverage
231-
uses: codecov/codecov-action@v4
231+
uses: codecov/codecov-action@v5
232232
with:
233233
fail_ci_if_error: false
234234
files: ./coverage.xml

.github/workflows/pythonapp-gpu.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,6 @@ jobs:
137137
shell: bash
138138
- name: Upload coverage
139139
if: ${{ github.head_ref != 'dev' && github.event.pull_request.merged != true }}
140-
uses: codecov/codecov-action@v4
140+
uses: codecov/codecov-action@v5
141141
with:
142142
files: ./coverage.xml

.github/workflows/setupapp.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ jobs:
7272
if pgrep python; then pkill python; fi
7373
shell: bash
7474
- name: Upload coverage
75-
uses: codecov/codecov-action@v4
75+
uses: codecov/codecov-action@v5
7676
with:
7777
fail_ci_if_error: false
7878
files: ./coverage.xml
@@ -119,7 +119,7 @@ jobs:
119119
BUILD_MONAI=1 ./runtests.sh --build --quick --min
120120
coverage xml --ignore-errors
121121
- name: Upload coverage
122-
uses: codecov/codecov-action@v4
122+
uses: codecov/codecov-action@v5
123123
with:
124124
fail_ci_if_error: false
125125
files: ./coverage.xml

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,9 @@ tests/testing_data/nrrd_example.nrrd
149149
# clang format tool
150150
.clang-format-bin/
151151

152+
# ctags
153+
tags
154+
152155
# VSCode
153156
.vscode/
154157
*.zip

docs/source/networks.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,11 @@ Nets
630630
.. autoclass:: ViTAutoEnc
631631
:members:
632632

633+
`MaskedAutoEncoderViT`
634+
~~~~~~~~~~~~~~~~~~~~~~
635+
.. autoclass:: MaskedAutoEncoderViT
636+
:members:
637+
633638
`FullyConnectedNet`
634639
~~~~~~~~~~~~~~~~~~~
635640
.. autoclass:: FullyConnectedNet

docs/source/transforms.rst

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,6 +1180,18 @@ Utility
11801180
:members:
11811181
:special-members: __call__
11821182

1183+
`TorchIO`
1184+
"""""""""
1185+
.. autoclass:: TorchIO
1186+
:members:
1187+
:special-members: __call__
1188+
1189+
`RandTorchIO`
1190+
"""""""""""""
1191+
.. autoclass:: RandTorchIO
1192+
:members:
1193+
:special-members: __call__
1194+
11831195
`MapLabelValue`
11841196
"""""""""""""""
11851197
.. autoclass:: MapLabelValue
@@ -2253,6 +2265,18 @@ Utility (Dict)
22532265
:members:
22542266
:special-members: __call__
22552267

2268+
`TorchIOd`
2269+
""""""""""
2270+
.. autoclass:: TorchIOd
2271+
:members:
2272+
:special-members: __call__
2273+
2274+
`RandTorchIOd`
2275+
""""""""""""""
2276+
.. autoclass:: RandTorchIOd
2277+
:members:
2278+
:special-members: __call__
2279+
22562280
`MapLabelValued`
22572281
""""""""""""""""
22582282
.. autoclass:: MapLabelValued

environment-dev.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ channels:
77
dependencies:
88
- numpy>=1.24,<2.0
99
- pytorch>=1.9
10+
- torchio
1011
- torchvision
1112
- pytorch-cuda>=11.6
1213
- pip

monai/bundle/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,4 @@
4343
MACRO_KEY,
4444
load_bundle_config,
4545
)
46-
from .workflows import BundleWorkflow, ConfigWorkflow
46+
from .workflows import BundleWorkflow, ConfigWorkflow, PythonicWorkflow

monai/bundle/workflows.py

Lines changed: 170 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,18 @@ class BundleWorkflow(ABC):
4444
workflow_type: specifies the workflow type: "train" or "training" for a training workflow,
4545
or "infer", "inference", "eval", "evaluation" for a inference workflow,
4646
other unsupported string will raise a ValueError.
47-
default to `train` for train workflow.
47+
default to `None` for only using meta properties.
4848
workflow: specifies the workflow type: "train" or "training" for a training workflow,
4949
or "infer", "inference", "eval", "evaluation" for a inference workflow,
5050
other unsupported string will raise a ValueError.
5151
default to `None` for common workflow.
52-
properties_path: the path to the JSON file of properties.
52+
properties_path: the path to the JSON file of properties. If `workflow_type` is specified, properties will be
53+
loaded from the file based on the provided `workflow_type` and meta. If no `workflow_type` is specified,
54+
properties will default to loading from "meta". If `properties_path` is None, default properties
55+
will be sourced from "monai/bundle/properties.py" based on the workflow_type:
56+
For a training workflow, properties load from `TrainProperties` and `MetaProperties`.
57+
For a inference workflow, properties load from `InferProperties` and `MetaProperties`.
58+
For workflow_type = None : only `MetaProperties` will be loaded.
5359
meta_file: filepath of the metadata file, if this is a list of file paths, their contents will be merged in order.
5460
logging_file: config file for `logging` module in the program. for more details:
5561
https://docs.python.org/3/library/logging.config.html#logging.config.fileConfig.
@@ -97,29 +103,50 @@ def __init__(
97103
meta_file = None
98104

99105
workflow_type = workflow if workflow is not None else workflow_type
100-
if workflow_type is None and properties_path is None:
101-
self.properties = copy(MetaProperties)
102-
self.workflow_type = None
103-
self.meta_file = meta_file
104-
return
106+
if workflow_type is not None:
107+
if workflow_type.lower() in self.supported_train_type:
108+
workflow_type = "train"
109+
elif workflow_type.lower() in self.supported_infer_type:
110+
workflow_type = "infer"
111+
else:
112+
raise ValueError(f"Unsupported workflow type: '{workflow_type}'.")
113+
105114
if properties_path is not None:
106115
properties_path = Path(properties_path)
107116
if not properties_path.is_file():
108117
raise ValueError(f"Property file {properties_path} does not exist.")
109118
with open(properties_path) as json_file:
110-
self.properties = json.load(json_file)
111-
self.workflow_type = None
112-
self.meta_file = meta_file
113-
return
114-
if workflow_type.lower() in self.supported_train_type: # type: ignore[union-attr]
115-
self.properties = {**TrainProperties, **MetaProperties}
116-
self.workflow_type = "train"
117-
elif workflow_type.lower() in self.supported_infer_type: # type: ignore[union-attr]
118-
self.properties = {**InferProperties, **MetaProperties}
119-
self.workflow_type = "infer"
119+
try:
120+
properties = json.load(json_file)
121+
self.properties: dict = {}
122+
if workflow_type is not None and workflow_type in properties:
123+
self.properties = properties[workflow_type]
124+
if "meta" in properties:
125+
self.properties.update(properties["meta"])
126+
elif workflow_type is None:
127+
if "meta" in properties:
128+
self.properties = properties["meta"]
129+
logger.info(
130+
"No workflow type specified, default to load meta properties from property file."
131+
)
132+
else:
133+
logger.warning("No 'meta' key found in properties while workflow_type is None.")
134+
except KeyError as e:
135+
raise ValueError(f"{workflow_type} not found in property file {properties_path}") from e
136+
except json.JSONDecodeError as e:
137+
raise ValueError(f"Error decoding JSON from property file {properties_path}") from e
120138
else:
121-
raise ValueError(f"Unsupported workflow type: '{workflow_type}'.")
139+
if workflow_type == "train":
140+
self.properties = {**TrainProperties, **MetaProperties}
141+
elif workflow_type == "infer":
142+
self.properties = {**InferProperties, **MetaProperties}
143+
elif workflow_type is None:
144+
self.properties = copy(MetaProperties)
145+
logger.info("No workflow type and property file specified, default to 'meta' properties.")
146+
else:
147+
raise ValueError(f"Unsupported workflow type: '{workflow_type}'.")
122148

149+
self.workflow_type = workflow_type
123150
self.meta_file = meta_file
124151

125152
@abstractmethod
@@ -226,6 +253,124 @@ def check_properties(self) -> list[str] | None:
226253
return [n for n, p in self.properties.items() if p.get(BundleProperty.REQUIRED, False) and not hasattr(self, n)]
227254

228255

256+
class PythonicWorkflow(BundleWorkflow):
257+
"""
258+
Base class for the pythonic workflow specification in bundle, it can be a training, evaluation or inference workflow.
259+
It defines the basic interfaces for the bundle workflow behavior: `initialize`, `finalize`, etc.
260+
This also provides the interface to get / set public properties to interact with a bundle workflow through
261+
defined `get_<property>` accessor methods or directly defining members of the object.
262+
For how to set the properties, users can define the `_set_<property>` methods or directly set the members of the object.
263+
The `initialize` method is called to set up the workflow before running. This method sets up internal state
264+
and prepares properties. If properties are modified after the workflow has been initialized, `self._is_initialized`
265+
is set to `False`. Before running the workflow again, `initialize` should be called to ensure that the workflow is
266+
properly set up with the new property values.
267+
268+
Args:
269+
workflow_type: specifies the workflow type: "train" or "training" for a training workflow,
270+
or "infer", "inference", "eval", "evaluation" for a inference workflow,
271+
other unsupported string will raise a ValueError.
272+
default to `None` for only using meta properties.
273+
workflow: specifies the workflow type: "train" or "training" for a training workflow,
274+
or "infer", "inference", "eval", "evaluation" for a inference workflow,
275+
other unsupported string will raise a ValueError.
276+
default to `None` for common workflow.
277+
properties_path: the path to the JSON file of properties. If `workflow_type` is specified, properties will be
278+
loaded from the file based on the provided `workflow_type` and meta. If no `workflow_type` is specified,
279+
properties will default to loading from "meta". If `properties_path` is None, default properties
280+
will be sourced from "monai/bundle/properties.py" based on the workflow_type:
281+
For a training workflow, properties load from `TrainProperties` and `MetaProperties`.
282+
For a inference workflow, properties load from `InferProperties` and `MetaProperties`.
283+
For workflow_type = None : only `MetaProperties` will be loaded.
284+
config_file: path to the config file, typically used to store hyperparameters.
285+
meta_file: filepath of the metadata file, if this is a list of file paths, their contents will be merged in order.
286+
logging_file: config file for `logging` module in the program. for more details:
287+
https://docs.python.org/3/library/logging.config.html#logging.config.fileConfig.
288+
289+
"""
290+
291+
supported_train_type: tuple = ("train", "training")
292+
supported_infer_type: tuple = ("infer", "inference", "eval", "evaluation")
293+
294+
def __init__(
295+
self,
296+
workflow_type: str | None = None,
297+
properties_path: PathLike | None = None,
298+
config_file: str | Sequence[str] | None = None,
299+
meta_file: str | Sequence[str] | None = None,
300+
logging_file: str | None = None,
301+
**override: Any,
302+
):
303+
meta_file = str(Path(os.getcwd()) / "metadata.json") if meta_file is None else meta_file
304+
super().__init__(
305+
workflow_type=workflow_type, properties_path=properties_path, meta_file=meta_file, logging_file=logging_file
306+
)
307+
self._props_vals: dict = {}
308+
self._set_props_vals: dict = {}
309+
self.parser = ConfigParser()
310+
if config_file is not None:
311+
self.parser.read_config(f=config_file)
312+
if self.meta_file is not None:
313+
self.parser.read_meta(f=self.meta_file)
314+
315+
# the rest key-values in the _args are to override config content
316+
self.parser.update(pairs=override)
317+
self._is_initialized: bool = False
318+
319+
def initialize(self, *args: Any, **kwargs: Any) -> Any:
320+
"""
321+
Initialize the bundle workflow before running.
322+
"""
323+
self._props_vals = {}
324+
self._is_initialized = True
325+
326+
def _get_property(self, name: str, property: dict) -> Any:
327+
"""
328+
With specified property name and information, get the expected property value.
329+
If the property is already generated, return from the bucket directly.
330+
If user explicitly set the property, return it directly.
331+
Otherwise, generate the expected property as a class private property with prefix "_".
332+
333+
Args:
334+
name: the name of target property.
335+
property: other information for the target property, defined in `TrainProperties` or `InferProperties`.
336+
"""
337+
if not self._is_initialized:
338+
raise RuntimeError("Please execute 'initialize' before getting any properties.")
339+
value = None
340+
if name in self._set_props_vals:
341+
value = self._set_props_vals[name]
342+
elif name in self._props_vals:
343+
value = self._props_vals[name]
344+
elif name in self.parser.config[self.parser.meta_key]: # type: ignore[index]
345+
id = self.properties.get(name, None).get(BundlePropertyConfig.ID, None)
346+
value = self.parser[id]
347+
else:
348+
try:
349+
value = getattr(self, f"get_{name}")()
350+
except AttributeError as e:
351+
if property[BundleProperty.REQUIRED]:
352+
raise ValueError(
353+
f"unsupported property '{name}' is required in the bundle properties,"
354+
f"need to implement a method 'get_{name}' to provide the property."
355+
) from e
356+
self._props_vals[name] = value
357+
return value
358+
359+
def _set_property(self, name: str, property: dict, value: Any) -> Any:
360+
"""
361+
With specified property name and information, set value for the expected property.
362+
Stores user-reset initialized objects that should not be re-initialized and marks the workflow as not initialized.
363+
364+
Args:
365+
name: the name of target property.
366+
property: other information for the target property, defined in `TrainProperties` or `InferProperties`.
367+
value: value to set for the property.
368+
369+
"""
370+
self._set_props_vals[name] = value
371+
self._is_initialized = False
372+
373+
229374
class ConfigWorkflow(BundleWorkflow):
230375
"""
231376
Specification for the config-based bundle workflow.
@@ -262,7 +407,13 @@ class ConfigWorkflow(BundleWorkflow):
262407
or "infer", "inference", "eval", "evaluation" for a inference workflow,
263408
other unsupported string will raise a ValueError.
264409
default to `None` for common workflow.
265-
properties_path: the path to the JSON file of properties.
410+
properties_path: the path to the JSON file of properties. If `workflow_type` is specified, properties will be
411+
loaded from the file based on the provided `workflow_type` and meta. If no `workflow_type` is specified,
412+
properties will default to loading from "train". If `properties_path` is None, default properties
413+
will be sourced from "monai/bundle/properties.py" based on the workflow_type:
414+
For a training workflow, properties load from `TrainProperties` and `MetaProperties`.
415+
For a inference workflow, properties load from `InferProperties` and `MetaProperties`.
416+
For workflow_type = None : only `MetaProperties` will be loaded.
266417
override: id-value pairs to override or add the corresponding config content.
267418
e.g. ``--net#input_chns 42``, ``--net %/data/other.json#net_arg``
268419
@@ -324,7 +475,6 @@ def __init__(
324475
self.parser.read_config(f=config_file)
325476
if self.meta_file is not None:
326477
self.parser.read_meta(f=self.meta_file)
327-
328478
# the rest key-values in the _args are to override config content
329479
self.parser.update(pairs=override)
330480
self.init_id = init_id

monai/networks/nets/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
from .generator import Generator
5454
from .highresnet import HighResBlock, HighResNet
5555
from .hovernet import Hovernet, HoVernet, HoVerNet, HoverNet
56+
from .masked_autoencoder_vit import MaskedAutoEncoderViT
5657
from .mednext import (
5758
MedNeXt,
5859
MedNext,

0 commit comments

Comments
 (0)