Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/source/handlers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Mean Dice metrics handler
.. automodule:: monai.handlers.mean_dice
:members:

Metrics logger
Metric logger
---------------
.. automodule:: monai.handlers.metric_logger
:members:
Expand Down
2 changes: 1 addition & 1 deletion docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Technical highlights
:maxdepth: 1
:caption: APIs

transform_api
transforms
losses
networks
metrics
Expand Down
44 changes: 40 additions & 4 deletions docs/source/transform_api.rst → docs/source/transforms.rst
Original file line number Diff line number Diff line change
Expand Up @@ -129,15 +129,21 @@ Vanilla Transforms
:members:
:special-members: __call__

`IntensityNormalizer`
`NormalizeIntensity`
~~~~~~~~~~~~~~~~~~~~~
.. autoclass:: IntensityNormalizer
.. autoclass:: NormalizeIntensity
:members:
:special-members: __call__

`ImageEndPadder`
`ScaleIntensityRange`
~~~~~~~~~~~~~~~~~~~~~
.. autoclass:: ScaleIntensityRange
:members:
:special-members: __call__

`PadImageEnd`
~~~~~~~~~~~~~~~~
.. autoclass:: ImageEndPadder
.. autoclass:: PadImageEnd
:members:
:special-members: __call__

Expand Down Expand Up @@ -183,12 +189,30 @@ Vanilla Transforms
:members:
:special-members: __call__

`Resample`
~~~~~~~~~~~
.. autoclass:: Resample
:members:
:special-members: __call__

`RandAffine`
~~~~~~~~~~~~
.. autoclass:: RandAffine
:members:
:special-members: __call__

`RandDeformGrid`
~~~~~~~~~~~~~~~~
.. autoclass:: RandDeformGrid
:members:
:special-members: __call__

`RandAffineGrid`
~~~~~~~~~~~~~~~~
.. autoclass:: RandAffineGrid
:members:
:special-members: __call__

`Rand2DElastic`
~~~~~~~~~~~~~~~
.. autoclass:: Rand2DElastic
Expand Down Expand Up @@ -262,6 +286,18 @@ Dictionary-based Composables
:members:
:special-members: __call__

`NormalizeIntensityd`
~~~~~~~~~~~~~~~~~~~~~
.. autoclass:: NormalizeIntensityd
:members:
:special-members: __call__

`ScaleIntensityRanged`
~~~~~~~~~~~~~~~~~~~~~~
.. autoclass:: ScaleIntensityRanged
:members:
:special-members: __call__

`RandRotate90d`
~~~~~~~~~~~~~~~
.. autoclass:: RandRotate90d
Expand Down
2 changes: 2 additions & 0 deletions docs/source/utils.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ Sliding window inference
Module utils
------------
.. automodule:: monai.utils.module
:members:

Aliases
-------
.. automodule:: monai.utils.aliases
:members:
2 changes: 1 addition & 1 deletion examples/classification_3d/densenet_training_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ def run_validation(engine):

# create a training data loader
train_ds = NiftiDataset(image_files=images[:10], labels=labels[:10], transform=train_transforms)
train_loader = DataLoader(train_ds, batch_size=2, num_workers=2, pin_memory=torch.cuda.is_available())
train_loader = DataLoader(train_ds, batch_size=2, shuffle=True, num_workers=2, pin_memory=torch.cuda.is_available())

train_epochs = 30
state = trainer.run(train_loader, train_epochs)
2 changes: 1 addition & 1 deletion examples/classification_3d/densenet_training_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ def run_validation(engine):

# create a training data loader
train_ds = monai.data.Dataset(data=train_files, transform=train_transforms)
train_loader = DataLoader(train_ds, batch_size=2, num_workers=4, pin_memory=torch.cuda.is_available())
train_loader = DataLoader(train_ds, batch_size=2, shuffle=True, num_workers=4, pin_memory=torch.cuda.is_available())

train_epochs = 30
state = trainer.run(train_loader, train_epochs)
523 changes: 523 additions & 0 deletions examples/integrate_to_spleen_program.ipynb

Large diffs are not rendered by default.

6 changes: 1 addition & 5 deletions examples/segmentation_3d/unet_training_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,11 @@

# create a training data loader
train_ds = NiftiDataset(images[:20], segs[:20], transform=train_imtrans, seg_transform=train_segtrans)
train_loader = DataLoader(train_ds, batch_size=5, num_workers=8, pin_memory=torch.cuda.is_available())
train_loader = DataLoader(train_ds, batch_size=5, shuffle=True, num_workers=8, pin_memory=torch.cuda.is_available())
# create a validation data loader
val_ds = NiftiDataset(images[-20:], segs[-20:], transform=val_imtrans, seg_transform=val_segtrans)
val_loader = DataLoader(val_ds, batch_size=5, num_workers=8, pin_memory=torch.cuda.is_available())


# Create UNet, DiceLoss and Adam optimizer
net = monai.networks.nets.UNet(
dimensions=3,
Expand All @@ -102,7 +101,6 @@
opt = torch.optim.Adam(net.parameters(), lr)
device = torch.device("cuda:0")


# ignite trainer expects batch=(img, seg) and returns output=loss at every iteration,
# user can add output_transform to return other values, like: y_pred, y, etc.
trainer = create_supervised_trainer(net, opt, loss, device, False)
Expand All @@ -123,7 +121,6 @@
train_tensorboard_stats_handler = TensorBoardStatsHandler()
train_tensorboard_stats_handler.attach(trainer)


validation_every_n_epochs = 1
# Set parameters for validation
metric_name = 'Mean_Dice'
Expand Down Expand Up @@ -168,6 +165,5 @@ def run_validation(engine):
)
evaluator.add_event_handler(event_name=Events.EPOCH_COMPLETED, handler=val_tensorboard_image_handler)


train_epochs = 30
state = trainer.run(train_loader, train_epochs)
9 changes: 2 additions & 7 deletions examples/segmentation_3d/unet_training_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,18 +78,16 @@
check_data = monai.utils.misc.first(check_loader)
print(check_data['img'].shape, check_data['seg'].shape)


# create a training data loader
train_ds = monai.data.Dataset(data=train_files, transform=train_transforms)
# use batch_size=2 to load images and use RandCropByPosNegLabeld to generate 2 x 4 images for network training
train_loader = DataLoader(train_ds, batch_size=2, num_workers=4, collate_fn=list_data_collate,
pin_memory=torch.cuda.is_available())
train_loader = DataLoader(train_ds, batch_size=2, shuffle=True, num_workers=4,
collate_fn=list_data_collate, pin_memory=torch.cuda.is_available())
# create a validation data loader
val_ds = monai.data.Dataset(data=val_files, transform=val_transforms)
val_loader = DataLoader(val_ds, batch_size=5, num_workers=8, collate_fn=list_data_collate,
pin_memory=torch.cuda.is_available())


# Create UNet, DiceLoss and Adam optimizer
net = monai.networks.nets.UNet(
dimensions=3,
Expand All @@ -104,7 +102,6 @@
opt = torch.optim.Adam(net.parameters(), lr)
device = torch.device("cuda:0")


# ignite trainer expects batch=(img, seg) and returns output=loss at every iteration,
# user can add output_transform to return other values, like: y_pred, y, etc.
def prepare_batch(batch, device=None, non_blocking=False):
Expand All @@ -129,7 +126,6 @@ def prepare_batch(batch, device=None, non_blocking=False):
train_tensorboard_stats_handler = TensorBoardStatsHandler()
train_tensorboard_stats_handler.attach(trainer)


validation_every_n_iters = 5
# Set parameters for validation
metric_name = 'Mean_Dice'
Expand Down Expand Up @@ -175,6 +171,5 @@ def run_validation(engine):
evaluator.add_event_handler(
event_name=Events.ITERATION_COMPLETED(every=2), handler=val_tensorboard_image_handler)


train_epochs = 5
state = trainer.run(train_loader, train_epochs)
4 changes: 2 additions & 2 deletions examples/unet_segmentation_3d.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@
"logging.basicConfig(stream=sys.stdout, level=logging.INFO)\n",
"\n",
"train_ds = NiftiDataset(images[:20], segs[:20], transform=imtrans, seg_transform=segtrans)\n",
"train_loader = DataLoader(train_ds, batch_size=5, num_workers=8, pin_memory=torch.cuda.is_available())\n",
"train_loader = DataLoader(train_ds, batch_size=5, shuffle=True, num_workers=8, pin_memory=torch.cuda.is_available())\n",
"\n",
"train_epochs = 30\n",
"state = trainer.run(train_loader, train_epochs)"
Expand Down Expand Up @@ -571,7 +571,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.4"
"version": "3.6.9"
}
},
"nbformat": 4,
Expand Down
72 changes: 66 additions & 6 deletions monai/transforms/composables.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
from monai.transforms.transforms import (LoadNifti, AsChannelFirst, Orientation,
AddChannel, Spacing, Rotate90, SpatialCrop,
RandAffine, Rand2DElastic, Rand3DElastic,
Rescale, Resize, Flip, Rotate, Zoom)
Rescale, Resize, Flip, Rotate, Zoom,
NormalizeIntensity, ScaleIntensityRange)
from monai.utils.misc import ensure_tuple
from monai.transforms.utils import generate_pos_neg_label_crop_centers, create_grid
from monai.utils.aliases import alias
Expand Down Expand Up @@ -117,7 +118,7 @@ class LoadNiftid(MapTransform):
dictionary-based wrapper of LoadNifti, must load image and metadata together.
"""

def __init__(self, keys, as_closest_canonical=False, dtype=None, meta_key_format='{}.{}', overwriting_keys=False):
def __init__(self, keys, as_closest_canonical=False, dtype=np.float32, meta_key_format='{}.{}', overwriting_keys=False):
"""
Args:
keys (hashable items): keys of the corresponding items to be transformed.
Expand Down Expand Up @@ -352,6 +353,57 @@ def __call__(self, data):
return d


@export
@alias('NormalizeIntensityD', 'NormalizeIntensityDict')
class NormalizeIntensityd(MapTransform):
"""
dictionary-based wrapper of NormalizeIntensity.

Args:
keys (hashable items): keys of the corresponding items to be transformed.
See also: monai.transform.composables.MapTransform
subtrahend (ndarray): the amount to subtract by (usually the mean)
divisor (ndarray): the amount to divide by (usually the standard deviation)
"""

def __init__(self, keys, subtrahend=None, divisor=None):
MapTransform.__init__(self, keys)
self.normalizer = NormalizeIntensity(subtrahend, divisor)

def __call__(self, data):
d = dict(data)
for key in self.keys:
d[key] = self.normalizer(d[key])
return d


@export
@alias('ScaleIntensityRangeD', 'ScaleIntensityRangeDict')
class ScaleIntensityRanged(MapTransform):
"""
dictionary-based wrapper of ScaleIntensityRange.

Args:
keys (hashable items): keys of the corresponding items to be transformed.
See also: monai.transform.composables.MapTransform
a_min (int or float): intensity original range min.
a_max (int or float): intensity original range max.
b_min (int or float): intensity target range min.
b_max (int or float): intensity target range max.
clip (bool): whether to perform clip after scaling.
"""

def __init__(self, keys, a_min, a_max, b_min, b_max, clip=False):
MapTransform.__init__(self, keys)
self.scaler = ScaleIntensityRange(a_min, a_max, b_min, b_max, clip)

def __call__(self, data):
d = dict(data)
for key in self.keys:
d[key] = self.scaler(d[key])
return d


@export
@alias('RandCropByPosNegLabelD', 'RandCropByPosNegLabelDict')
class RandCropByPosNegLabeld(Randomizable, MapTransform):
Expand All @@ -369,9 +421,13 @@ class RandCropByPosNegLabeld(Randomizable, MapTransform):
neg (int, float): used to calculate the ratio ``pos / (pos + neg)`` for the probability to pick a
foreground voxel as a center rather than a background voxel.
num_samples (int): number of samples (crop regions) to take in each list.
image_key (str): if image_key is not None, use ``label == 0 & image > image_threshold`` to select
the negative sample(background) center. so the crop center will only exist on valid image area.
image_threshold (int or float): if enabled image_key, use ``image > image_threshold`` to determine
the valid image content area.
"""

def __init__(self, keys, label_key, size, pos=1, neg=1, num_samples=1):
def __init__(self, keys, label_key, size, pos=1, neg=1, num_samples=1, image_key=None, image_threshold=0):
MapTransform.__init__(self, keys)
assert isinstance(label_key, str), 'label_key must be a string.'
assert isinstance(size, (list, tuple)), 'size must be list or tuple.'
Expand All @@ -385,15 +441,19 @@ def __init__(self, keys, label_key, size, pos=1, neg=1, num_samples=1):
self.size = size
self.pos_ratio = float(pos) / (float(pos) + float(neg))
self.num_samples = num_samples
self.image_key = image_key
self.image_threshold = image_threshold
self.centers = None

def randomize(self, label):
self.centers = generate_pos_neg_label_crop_centers(label, self.size, self.num_samples, self.pos_ratio, self.R)
def randomize(self, label, image):
self.centers = generate_pos_neg_label_crop_centers(label, self.size, self.num_samples, self.pos_ratio,
image, self.image_threshold, self.R)

def __call__(self, data):
d = dict(data)
label = d[self.label_key]
self.randomize(label)
image = d[self.image_key] if self.image_key else None
self.randomize(label, image)
results = [dict() for _ in range(self.num_samples)]
for key in data.keys():
if key in self.keys:
Expand Down
2 changes: 0 additions & 2 deletions monai/transforms/compose.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,6 @@ def set_random_state(self, seed=None, state=None):

Returns:
a Randomizable instance.
Note:
thread safety
"""
if seed is not None:
_seed = id(seed) if not isinstance(seed, int) else seed
Expand Down
Loading