Skip to content

Commit a56c0c3

Browse files
Merge branch 'dev' into dev
2 parents 9377b63 + 401ea4a commit a56c0c3

File tree

92 files changed

+569
-643
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

92 files changed

+569
-643
lines changed

SECURITY.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Security Policy
2+
3+
## Reporting a Vulnerability
4+
MONAI takes security seriously and appreciate your efforts to responsibly disclose vulnerabilities. If you discover a security issue, please report it as soon as possible.
5+
6+
To report a security issue:
7+
* please use the GitHub Security Advisories tab to "[Open a draft security advisory](https://github.com/Project-MONAI/MONAI/security/advisories/new)".
8+
* Include a detailed description of the issue, steps to reproduce, potential impact, and any possible mitigations.
9+
* If applicable, please also attach proof-of-concept code or screenshots.
10+
* We aim to acknowledge your report within 72 hours and provide a status update as we investigate.
11+
* Please do not create public issues for security-related reports.
12+
13+
## Disclosure Policy
14+
* We follow a coordinated disclosure approach.
15+
* We will not publicly disclose vulnerabilities until a fix has been developed and released.
16+
* Credit will be given to researchers who responsibly disclose vulnerabilities, if requested.
17+
## Acknowledgements
18+
We greatly appreciate contributions from the security community and strive to recognize all researchers who help keep MONAI safe.

docs/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,6 @@ onnx>=1.13.0
4040
onnxruntime; python_version <= '3.10'
4141
zarr
4242
huggingface_hub
43-
pyamg>=5.0.0
43+
pyamg>=5.0.0, <5.3.0
4444
packaging
4545
polygraphy

monai/apps/auto3dseg/auto_runner.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,15 @@ class AutoRunner:
194194
├── segresnet2d_0 # network scripts/configs/checkpoints and pickle object of the algo
195195
└── swinunetr_0 # network scripts/configs/checkpoints and pickle object of the algo
196196
197+
198+
The input config requires at least the following keys:
199+
- ``modality``: the modality of the data, e.g. "ct", "mri", etc.
200+
- ``datalist``: the path to the datalist file in JSON format.
201+
- ``dataroot``: the root directory of the data files.
202+
203+
For the datalist file format, see the description under :py:func:`monai.data.load_decathlon_datalist`.
204+
Note that the AutoRunner will use the "validation" key in the datalist file if it exists, otherwise
205+
it will do cross-validation, by default with five folds (this is hardcoded).
197206
"""
198207

199208
analyze_params: dict | None

monai/data/decathlon_datalist.py

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,42 @@ def load_decathlon_datalist(
9292
) -> list[dict]:
9393
"""Load image/label paths of decathlon challenge from JSON file
9494
95-
Json file is similar to what you get from http://medicaldecathlon.com/
96-
Those dataset.json files
95+
JSON file should follow the format of the Medical Segmentation Decathlon
96+
datalist.json files, see http://medicaldecathlon.com.
97+
The files are structured as follows:
98+
99+
.. code-block:: python
100+
101+
{
102+
"metadata_key_0": "metadata_value_0",
103+
"metadata_key_1": "metadata_value_1",
104+
...,
105+
"training": [
106+
{"image": "path/to/image_1.nii.gz", "label": "path/to/label_1.nii.gz"},
107+
{"image": "path/to/image_2.nii.gz", "label": "path/to/label_2.nii.gz"},
108+
...
109+
],
110+
"test": [
111+
"path/to/image_3.nii.gz",
112+
"path/to/image_4.nii.gz",
113+
...
114+
]
115+
}
116+
117+
118+
The metadata keys are optional for loading the datalist, but include:
119+
- some string items: ``name``, ``description``, ``reference``, ``licence``, ``release``, ``tensorImageSize``
120+
- two dict items: ``modality`` (keyed by channel index), and ``labels`` (keyed by label index)
121+
- and two integer items: ``numTraining`` and ``numTest``, with the number of items.
122+
123+
The ``training`` key contains a list of dictionaries, each of which has at least
124+
the ``image`` and ``label`` keys.
125+
The image and label are loaded by :py:func:`monai.transforms.LoadImaged`, so both can be either
126+
a single file path or a list of file paths, in which case they are loaded as multi-channel images.
127+
Each item can also include a ``fold`` key for cross-validation purposes.
128+
The "test" key contains a list of image paths, without labels, MONAI also supports a "validation" list
129+
with the same format as the "training" list.
130+
97131
98132
Args:
99133
data_list_file_path: the path to the json file of datalist.
@@ -107,11 +141,11 @@ def load_decathlon_datalist(
107141
108142
Returns a list of data items, each of which is a dict keyed by element names, for example:
109143
110-
.. code-block::
144+
.. code-block:: python
111145
112146
[
113-
{'image': '/workspace/data/chest_19.nii.gz', 'label': 0},
114-
{'image': '/workspace/data/chest_31.nii.gz', 'label': 1}
147+
{'image': '/workspace/data/chest_19.nii.gz', 'label': '/workspace/labels/chest_19.nii.gz'},
148+
{'image': '/workspace/data/chest_31.nii.gz', 'label': '/workspace/labels/chest_31.nii.gz'},
115149
]
116150
117151
"""
@@ -134,7 +168,8 @@ def load_decathlon_datalist(
134168

135169

136170
def load_decathlon_properties(data_property_file_path: PathLike, property_keys: Sequence[str] | str) -> dict:
137-
"""Load the properties from the JSON file contains data property with specified `property_keys`.
171+
"""Extract the properties with the specified keys from the Decathlon JSON file.
172+
See under `load_decathlon_datalist` for the expected keys in the Decathlon challenge.
138173
139174
Args:
140175
data_property_file_path: the path to the JSON file of data properties.

monai/inferers/inferer.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -916,6 +916,7 @@ def sample(
916916
verbose: bool = True,
917917
seg: torch.Tensor | None = None,
918918
cfg: float | None = None,
919+
cfg_fill_value: float = -1.0,
919920
) -> torch.Tensor | tuple[torch.Tensor, list[torch.Tensor]]:
920921
"""
921922
Args:
@@ -929,6 +930,7 @@ def sample(
929930
verbose: if true, prints the progression bar of the sampling process.
930931
seg: if diffusion model is instance of SPADEDiffusionModel, segmentation must be provided.
931932
cfg: classifier-free-guidance scale, which indicates the level of strengthening on the conditioning.
933+
cfg_fill_value: the fill value to use for the unconditioned input when using classifier-free guidance.
932934
"""
933935
if mode not in ["crossattn", "concat"]:
934936
raise NotImplementedError(f"{mode} condition is not supported")
@@ -961,7 +963,7 @@ def sample(
961963
model_input = torch.cat([image] * 2, dim=0)
962964
if conditioning is not None:
963965
uncondition = torch.ones_like(conditioning)
964-
uncondition.fill_(-1)
966+
uncondition.fill_(cfg_fill_value)
965967
conditioning_input = torch.cat([uncondition, conditioning], dim=0)
966968
else:
967969
conditioning_input = None
@@ -1261,6 +1263,7 @@ def sample( # type: ignore[override]
12611263
verbose: bool = True,
12621264
seg: torch.Tensor | None = None,
12631265
cfg: float | None = None,
1266+
cfg_fill_value: float = -1.0,
12641267
) -> torch.Tensor | tuple[torch.Tensor, list[torch.Tensor]]:
12651268
"""
12661269
Args:
@@ -1276,6 +1279,7 @@ def sample( # type: ignore[override]
12761279
seg: if diffusion model is instance of SPADEDiffusionModel, or autoencoder_model
12771280
is instance of SPADEAutoencoderKL, segmentation must be provided.
12781281
cfg: classifier-free-guidance scale, which indicates the level of strengthening on the conditioning.
1282+
cfg_fill_value: the fill value to use for the unconditioned input when using classifier-free guidance.
12791283
"""
12801284

12811285
if (
@@ -1300,6 +1304,7 @@ def sample( # type: ignore[override]
13001304
verbose=verbose,
13011305
seg=seg,
13021306
cfg=cfg,
1307+
cfg_fill_value=cfg_fill_value,
13031308
)
13041309

13051310
if save_intermediates:
@@ -1479,6 +1484,7 @@ def sample( # type: ignore[override]
14791484
verbose: bool = True,
14801485
seg: torch.Tensor | None = None,
14811486
cfg: float | None = None,
1487+
cfg_fill_value: float = -1.0,
14821488
) -> torch.Tensor | tuple[torch.Tensor, list[torch.Tensor]]:
14831489
"""
14841490
Args:
@@ -1493,7 +1499,8 @@ def sample( # type: ignore[override]
14931499
mode: Conditioning mode for the network.
14941500
verbose: if true, prints the progression bar of the sampling process.
14951501
seg: if diffusion model is instance of SPADEDiffusionModel, segmentation must be provided.
1496-
cfg: classifier-free-guidance scale, which indicates the level of strengthening on the conditioning.
1502+
cfg: classifier-free-guidance scale, which indicates the level of strengthening on the conditioning.
1503+
cfg_fill_value: the fill value to use for the unconditioned input when using classifier-free guidance.
14971504
"""
14981505
if mode not in ["crossattn", "concat"]:
14991506
raise NotImplementedError(f"{mode} condition is not supported")
@@ -1521,7 +1528,7 @@ def sample( # type: ignore[override]
15211528
model_input = torch.cat([image] * 2, dim=0)
15221529
if conditioning is not None:
15231530
uncondition = torch.ones_like(conditioning)
1524-
uncondition.fill_(-1)
1531+
uncondition.fill_(cfg_fill_value)
15251532
conditioning_input = torch.cat([uncondition, conditioning], dim=0)
15261533
else:
15271534
conditioning_input = None
@@ -1839,6 +1846,7 @@ def sample( # type: ignore[override]
18391846
verbose: bool = True,
18401847
seg: torch.Tensor | None = None,
18411848
cfg: float | None = None,
1849+
cfg_fill_value: float = -1.0,
18421850
) -> torch.Tensor | tuple[torch.Tensor, list[torch.Tensor]]:
18431851
"""
18441852
Args:
@@ -1856,6 +1864,7 @@ def sample( # type: ignore[override]
18561864
seg: if diffusion model is instance of SPADEDiffusionModel, or autoencoder_model
18571865
is instance of SPADEAutoencoderKL, segmentation must be provided.
18581866
cfg: classifier-free-guidance scale, which indicates the level of strengthening on the conditioning.
1867+
cfg_fill_value: the fill value to use for the unconditioned input when using classifier-free guidance.
18591868
"""
18601869

18611870
if (
@@ -1884,6 +1893,7 @@ def sample( # type: ignore[override]
18841893
verbose=verbose,
18851894
seg=seg,
18861895
cfg=cfg,
1896+
cfg_fill_value=cfg_fill_value,
18871897
)
18881898

18891899
if save_intermediates:

monai/networks/blocks/fcn.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,9 @@ def __init__(
123123
self.upsample_mode = upsample_mode
124124
self.conv2d_type = conv2d_type
125125
self.out_channels = out_channels
126-
resnet = models.resnet50(pretrained=pretrained, progress=progress)
126+
resnet = models.resnet50(
127+
progress=progress, weights=models.ResNet50_Weights.IMAGENET1K_V1 if pretrained else None
128+
)
127129

128130
self.conv1 = resnet.conv1
129131
self.bn0 = resnet.bn1

monai/networks/nets/diffusion_model_unet.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333

3434
import math
3535
from collections.abc import Sequence
36+
from typing import Optional
3637

3738
import torch
3839
from torch import nn
@@ -2006,7 +2007,7 @@ def __init__(
20062007

20072008
self.down_blocks.append(down_block)
20082009

2009-
self.out = nn.Sequential(nn.Linear(4096, 512), nn.ReLU(), nn.Dropout(0.1), nn.Linear(512, self.out_channels))
2010+
self.out: Optional[nn.Module] = None
20102011

20112012
def forward(
20122013
self,
@@ -2049,6 +2050,12 @@ def forward(
20492050
h, _ = downsample_block(hidden_states=h, temb=emb, context=context)
20502051

20512052
h = h.reshape(h.shape[0], -1)
2053+
2054+
# 5. out
2055+
if self.out is None:
2056+
self.out = nn.Sequential(
2057+
nn.Linear(h.shape[1], 512), nn.ReLU(), nn.Dropout(0.1), nn.Linear(512, self.out_channels)
2058+
)
20522059
output: torch.Tensor = self.out(h)
20532060

20542061
return output

monai/networks/nets/milmodel.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import torch
1717
import torch.nn as nn
1818

19-
from monai.utils.module import optional_import
19+
from monai.utils import optional_import
2020

2121
models, _ = optional_import("torchvision.models")
2222

@@ -48,7 +48,6 @@ class MILModel(nn.Module):
4848
Defaults to ``None`` (necessary only when using a custom backbone)
4949
trans_blocks: number of the blocks in `TransformEncoder` layer.
5050
trans_dropout: dropout rate in `TransformEncoder` layer.
51-
5251
"""
5352

5453
def __init__(
@@ -74,7 +73,7 @@ def __init__(
7473
self.transformer: nn.Module | None = None
7574

7675
if backbone is None:
77-
net = models.resnet50(pretrained=pretrained)
76+
net = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V1 if pretrained else None)
7877
nfc = net.fc.in_features # save the number of final features
7978
net.fc = torch.nn.Identity() # remove final linear layer
8079

@@ -99,7 +98,7 @@ def hook(module, input, output):
9998
torch_model = getattr(models, backbone, None)
10099
if torch_model is None:
101100
raise ValueError("Unknown torch vision model" + str(backbone))
102-
net = torch_model(pretrained=pretrained)
101+
net = torch_model(weights="DEFAULT" if pretrained else None)
103102

104103
if getattr(net, "fc", None) is not None:
105104
nfc = net.fc.in_features # save the number of final features

monai/networks/nets/torchvision_fc.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,11 @@ def __init__(
112112
weights=None,
113113
**kwargs,
114114
):
115-
if weights is not None:
116-
model = getattr(models, model_name)(weights=weights, **kwargs)
117-
elif pretrained:
118-
model = getattr(models, model_name)(weights="DEFAULT", **kwargs)
119-
else:
120-
model = getattr(models, model_name)(weights=None, **kwargs)
115+
# if pretrained is False, weights is a weight tensor or None for no pretrained loading
116+
if pretrained and weights is None:
117+
weights = "DEFAULT"
118+
119+
model = getattr(models, model_name)(weights=weights, **kwargs)
121120

122121
super().__init__(
123122
model=model,

monai/transforms/intensity/dictionary.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1836,7 +1836,7 @@ class HistogramNormalized(MapTransform):
18361836
See also: :py:class:`monai.transforms.compose.MapTransform`
18371837
num_bins: number of the bins to use in histogram, default to `256`. for more details:
18381838
https://numpy.org/doc/stable/reference/generated/numpy.histogram.html.
1839-
min: the min value to normalize input image, default to `255`.
1839+
min: the min value to normalize input image, default to `0`.
18401840
max: the max value to normalize input image, default to `255`.
18411841
mask: if provided, must be ndarray of bools or 0s and 1s, and same shape as `image`.
18421842
only points at which `mask==True` are used for the equalization.

0 commit comments

Comments
 (0)