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 fastMONAI/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.6.1"
__version__ = "0.6.3"
24 changes: 16 additions & 8 deletions fastMONAI/_modidx.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@
'fastMONAI/vision_augmentation.py'),
'fastMONAI.vision_augmentation.NormalizeIntensity.encodes': ( 'vision_augment.html#normalizeintensity.encodes',
'fastMONAI/vision_augmentation.py'),
'fastMONAI.vision_augmentation.NormalizeIntensity.tio_transform': ( 'vision_augment.html#normalizeintensity.tio_transform',
'fastMONAI/vision_augmentation.py'),
'fastMONAI.vision_augmentation.OneOf': ( 'vision_augment.html#oneof',
'fastMONAI/vision_augmentation.py'),
'fastMONAI.vision_augmentation.OneOf.__init__': ( 'vision_augment.html#oneof.__init__',
Expand Down Expand Up @@ -199,6 +201,8 @@
'fastMONAI/vision_augmentation.py'),
'fastMONAI.vision_augmentation.RandomIntensityScale.encodes': ( 'vision_augment.html#randomintensityscale.encodes',
'fastMONAI/vision_augmentation.py'),
'fastMONAI.vision_augmentation.RandomIntensityScale.tio_transform': ( 'vision_augment.html#randomintensityscale.tio_transform',
'fastMONAI/vision_augmentation.py'),
'fastMONAI.vision_augmentation.RandomMotion': ( 'vision_augment.html#randommotion',
'fastMONAI/vision_augmentation.py'),
'fastMONAI.vision_augmentation.RandomMotion.__init__': ( 'vision_augment.html#randommotion.__init__',
Expand Down Expand Up @@ -231,14 +235,6 @@
'fastMONAI/vision_augmentation.py'),
'fastMONAI.vision_augmentation.RescaleIntensity.tio_transform': ( 'vision_augment.html#rescaleintensity.tio_transform',
'fastMONAI/vision_augmentation.py'),
'fastMONAI.vision_augmentation.SpatialPad': ( 'vision_augment.html#spatialpad',
'fastMONAI/vision_augmentation.py'),
'fastMONAI.vision_augmentation.SpatialPad.__init__': ( 'vision_augment.html#spatialpad.__init__',
'fastMONAI/vision_augmentation.py'),
'fastMONAI.vision_augmentation.SpatialPad.encodes': ( 'vision_augment.html#spatialpad.encodes',
'fastMONAI/vision_augmentation.py'),
'fastMONAI.vision_augmentation.SpatialPad.tio_transform': ( 'vision_augment.html#spatialpad.tio_transform',
'fastMONAI/vision_augmentation.py'),
'fastMONAI.vision_augmentation.ZNormalization': ( 'vision_augment.html#znormalization',
'fastMONAI/vision_augmentation.py'),
'fastMONAI.vision_augmentation.ZNormalization.__init__': ( 'vision_augment.html#znormalization.__init__',
Expand All @@ -247,6 +243,12 @@
'fastMONAI/vision_augmentation.py'),
'fastMONAI.vision_augmentation.ZNormalization.tio_transform': ( 'vision_augment.html#znormalization.tio_transform',
'fastMONAI/vision_augmentation.py'),
'fastMONAI.vision_augmentation._TioNormalizeIntensity': ( 'vision_augment.html#_tionormalizeintensity',
'fastMONAI/vision_augmentation.py'),
'fastMONAI.vision_augmentation._TioNormalizeIntensity.__init__': ( 'vision_augment.html#_tionormalizeintensity.__init__',
'fastMONAI/vision_augmentation.py'),
'fastMONAI.vision_augmentation._TioNormalizeIntensity.apply_transform': ( 'vision_augment.html#_tionormalizeintensity.apply_transform',
'fastMONAI/vision_augmentation.py'),
'fastMONAI.vision_augmentation._TioRandomCutout': ( 'vision_augment.html#_tiorandomcutout',
'fastMONAI/vision_augmentation.py'),
'fastMONAI.vision_augmentation._TioRandomCutout.__init__': ( 'vision_augment.html#_tiorandomcutout.__init__',
Expand All @@ -255,6 +257,12 @@
'fastMONAI/vision_augmentation.py'),
'fastMONAI.vision_augmentation._TioRandomCutout.apply_transform': ( 'vision_augment.html#_tiorandomcutout.apply_transform',
'fastMONAI/vision_augmentation.py'),
'fastMONAI.vision_augmentation._TioRandomIntensityScale': ( 'vision_augment.html#_tiorandomintensityscale',
'fastMONAI/vision_augmentation.py'),
'fastMONAI.vision_augmentation._TioRandomIntensityScale.__init__': ( 'vision_augment.html#_tiorandomintensityscale.__init__',
'fastMONAI/vision_augmentation.py'),
'fastMONAI.vision_augmentation._TioRandomIntensityScale.apply_transform': ( 'vision_augment.html#_tiorandomintensityscale.apply_transform',
'fastMONAI/vision_augmentation.py'),
'fastMONAI.vision_augmentation._create_ellipsoid_mask': ( 'vision_augment.html#_create_ellipsoid_mask',
'fastMONAI/vision_augmentation.py'),
'fastMONAI.vision_augmentation.do_pad_or_crop': ( 'vision_augment.html#do_pad_or_crop',
Expand Down
43 changes: 31 additions & 12 deletions fastMONAI/dataset_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,12 +319,17 @@ def suggest_patch_size(
max_patch_size: list = None,
divisor: int = 16
) -> list:
"""Suggest optimal patch size based on median image dimensions.
"""Suggest optimal patch size based on dataset dimensions.

Uses median shape as the starting point but clamps to the minimum
volume size per axis, ensuring the suggested patch fits ALL volumes
without requiring padding during training.

Algorithm:
1. Use median shape for robustness to outliers
2. Round down to nearest multiple of divisor (16 for 4+ UNet pooling layers)
3. Clamp to [min_patch_size, max_patch_size]
1. Use min(median, min_volume) per axis for safety
2. Round down to nearest multiple of divisor (16 for UNet compatibility)
3. Clamp to [min_patch_size, max_patch_size] bounds
4. Validate: error if min_patch_size exceeds smallest volume

Args:
dataset: MedDataset instance with analyzed images.
Expand Down Expand Up @@ -359,30 +364,44 @@ def suggest_patch_size(
# Get size statistics (resampled to target_spacing)
stats = dataset.get_size_statistics(target_spacing)
median_shape = stats['median']
min_shape = stats['min']

# Handle single-image edge case
if len(dataset.df) == 1:
warnings.warn("Single image dataset - using image dimensions directly")

# Step 1: Round down to nearest divisor
def round_to_divisor(val, div):
"""Round down to nearest multiple of divisor."""
return max(div, int(val // div) * div)

patch_size = [round_to_divisor(dim, divisor) for dim in median_shape]
# Step 1: Clamp to min(median, min_volume) per axis — safety guarantee
effective_dims = [min(med, mn) for med, mn in zip(median_shape, min_shape)]

# Step 2: Round down to nearest divisor
patch_size = [round_to_divisor(dim, divisor) for dim in effective_dims]

# Step 2: Clamp to bounds
# Step 3: Clamp to [min_patch_size, max_patch_size] bounds
patch_size = [
max(min_p, min(max_p, p))
for p, min_p, max_p in zip(patch_size, min_patch_size, max_patch_size)
]

# Edge case: image smaller than suggested patch
for i, (p, median_dim) in enumerate(zip(patch_size, median_shape)):
if median_dim < p:
# Step 4: Final safety check — error if bounds force patch > min volume
for i, (p, min_dim) in enumerate(zip(patch_size, min_shape)):
if p > min_dim:
raise ValueError(
f"Cannot suggest safe patch_size for dimension {i}: "
f"smallest volume is {min_dim:.0f} but min_patch_size={min_patch_size[i]}. "
f"Exclude small volumes or reduce min_patch_size."
)

# Warn when suggestion was reduced to fit smallest volume
for i, (med, mn, p) in enumerate(zip(median_shape, min_shape, patch_size)):
median_based = min(max_patch_size[i], round_to_divisor(med, divisor))
if p < median_based:
warnings.warn(
f"Median dimension {i} ({median_dim:.0f}) smaller than suggested "
f"patch_size ({p}). Images will require padding."
f"Dim {i}: patch_size reduced from {median_based} to {p} "
f"to fit smallest volume (min={mn:.0f}, median={med:.0f})."
)

return patch_size
Expand Down
Loading