fastMONAI 0.5.4__py3-none-any.whl → 0.6.0__py3-none-any.whl

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.
fastMONAI/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.5.4"
1
+ __version__ = "0.6.0"
fastMONAI/_modidx.py CHANGED
@@ -16,6 +16,8 @@ d = { 'settings': { 'branch': 'main',
16
16
  'fastMONAI/dataset_info.py'),
17
17
  'fastMONAI.dataset_info.MedDataset.calculate_target_size': ( 'dataset_info.html#meddataset.calculate_target_size',
18
18
  'fastMONAI/dataset_info.py'),
19
+ 'fastMONAI.dataset_info.MedDataset.get_size_statistics': ( 'dataset_info.html#meddataset.get_size_statistics',
20
+ 'fastMONAI/dataset_info.py'),
19
21
  'fastMONAI.dataset_info.MedDataset.get_suggestion': ( 'dataset_info.html#meddataset.get_suggestion',
20
22
  'fastMONAI/dataset_info.py'),
21
23
  'fastMONAI.dataset_info.MedDataset.get_volume_summary': ( 'dataset_info.html#meddataset.get_volume_summary',
@@ -25,7 +27,9 @@ d = { 'settings': { 'branch': 'main',
25
27
  'fastMONAI.dataset_info.MedDataset.visualize_cases': ( 'dataset_info.html#meddataset.visualize_cases',
26
28
  'fastMONAI/dataset_info.py'),
27
29
  'fastMONAI.dataset_info.get_class_weights': ( 'dataset_info.html#get_class_weights',
28
- 'fastMONAI/dataset_info.py')},
30
+ 'fastMONAI/dataset_info.py'),
31
+ 'fastMONAI.dataset_info.suggest_patch_size': ( 'dataset_info.html#suggest_patch_size',
32
+ 'fastMONAI/dataset_info.py')},
29
33
  'fastMONAI.external_data': { 'fastMONAI.external_data.MURLs': ('external_data.html#murls', 'fastMONAI/external_data.py'),
30
34
  'fastMONAI.external_data._create_nodule_df': ( 'external_data.html#_create_nodule_df',
31
35
  'fastMONAI/external_data.py'),
@@ -87,6 +91,14 @@ d = { 'settings': { 'branch': 'main',
87
91
  'fastMONAI/utils.py'),
88
92
  'fastMONAI.utils.ModelTrackingCallback.extract_all_params': ( 'utils.html#modeltrackingcallback.extract_all_params',
89
93
  'fastMONAI/utils.py'),
94
+ 'fastMONAI.utils._detect_patch_workflow': ('utils.html#_detect_patch_workflow', 'fastMONAI/utils.py'),
95
+ 'fastMONAI.utils._extract_loss_name': ('utils.html#_extract_loss_name', 'fastMONAI/utils.py'),
96
+ 'fastMONAI.utils._extract_model_name': ('utils.html#_extract_model_name', 'fastMONAI/utils.py'),
97
+ 'fastMONAI.utils._extract_patch_config': ('utils.html#_extract_patch_config', 'fastMONAI/utils.py'),
98
+ 'fastMONAI.utils._extract_size_from_transforms': ( 'utils.html#_extract_size_from_transforms',
99
+ 'fastMONAI/utils.py'),
100
+ 'fastMONAI.utils._extract_standard_config': ('utils.html#_extract_standard_config', 'fastMONAI/utils.py'),
101
+ 'fastMONAI.utils.create_mlflow_callback': ('utils.html#create_mlflow_callback', 'fastMONAI/utils.py'),
90
102
  'fastMONAI.utils.load_patch_variables': ('utils.html#load_patch_variables', 'fastMONAI/utils.py'),
91
103
  'fastMONAI.utils.load_variables': ('utils.html#load_variables', 'fastMONAI/utils.py'),
92
104
  'fastMONAI.utils.print_colab_gpu_info': ('utils.html#print_colab_gpu_info', 'fastMONAI/utils.py'),
@@ -147,6 +159,16 @@ d = { 'settings': { 'branch': 'main',
147
159
  'fastMONAI/vision_augmentation.py'),
148
160
  'fastMONAI.vision_augmentation.RandomBlur.tio_transform': ( 'vision_augment.html#randomblur.tio_transform',
149
161
  'fastMONAI/vision_augmentation.py'),
162
+ 'fastMONAI.vision_augmentation.RandomCutout': ( 'vision_augment.html#randomcutout',
163
+ 'fastMONAI/vision_augmentation.py'),
164
+ 'fastMONAI.vision_augmentation.RandomCutout.__init__': ( 'vision_augment.html#randomcutout.__init__',
165
+ 'fastMONAI/vision_augmentation.py'),
166
+ 'fastMONAI.vision_augmentation.RandomCutout._get_fill_value': ( 'vision_augment.html#randomcutout._get_fill_value',
167
+ 'fastMONAI/vision_augmentation.py'),
168
+ 'fastMONAI.vision_augmentation.RandomCutout.encodes': ( 'vision_augment.html#randomcutout.encodes',
169
+ 'fastMONAI/vision_augmentation.py'),
170
+ 'fastMONAI.vision_augmentation.RandomCutout.tio_transform': ( 'vision_augment.html#randomcutout.tio_transform',
171
+ 'fastMONAI/vision_augmentation.py'),
150
172
  'fastMONAI.vision_augmentation.RandomElasticDeformation': ( 'vision_augment.html#randomelasticdeformation',
151
173
  'fastMONAI/vision_augmentation.py'),
152
174
  'fastMONAI.vision_augmentation.RandomElasticDeformation.__init__': ( 'vision_augment.html#randomelasticdeformation.__init__',
@@ -217,6 +239,16 @@ d = { 'settings': { 'branch': 'main',
217
239
  'fastMONAI/vision_augmentation.py'),
218
240
  'fastMONAI.vision_augmentation.ZNormalization.tio_transform': ( 'vision_augment.html#znormalization.tio_transform',
219
241
  'fastMONAI/vision_augmentation.py'),
242
+ 'fastMONAI.vision_augmentation._TioRandomCutout': ( 'vision_augment.html#_tiorandomcutout',
243
+ 'fastMONAI/vision_augmentation.py'),
244
+ 'fastMONAI.vision_augmentation._TioRandomCutout.__init__': ( 'vision_augment.html#_tiorandomcutout.__init__',
245
+ 'fastMONAI/vision_augmentation.py'),
246
+ 'fastMONAI.vision_augmentation._TioRandomCutout._apply_cutout': ( 'vision_augment.html#_tiorandomcutout._apply_cutout',
247
+ 'fastMONAI/vision_augmentation.py'),
248
+ 'fastMONAI.vision_augmentation._TioRandomCutout.apply_transform': ( 'vision_augment.html#_tiorandomcutout.apply_transform',
249
+ 'fastMONAI/vision_augmentation.py'),
250
+ 'fastMONAI.vision_augmentation._create_ellipsoid_mask': ( 'vision_augment.html#_create_ellipsoid_mask',
251
+ 'fastMONAI/vision_augmentation.py'),
220
252
  'fastMONAI.vision_augmentation.do_pad_or_crop': ( 'vision_augment.html#do_pad_or_crop',
221
253
  'fastMONAI/vision_augmentation.py')},
222
254
  'fastMONAI.vision_core': { 'fastMONAI.vision_core.MedBase': ('vision_core.html#medbase', 'fastMONAI/vision_core.py'),
@@ -353,12 +385,20 @@ d = { 'settings': { 'branch': 'main',
353
385
  'fastMONAI/vision_metrics.py')},
354
386
  'fastMONAI.vision_patch': { 'fastMONAI.vision_patch.MedPatchDataLoader': ( 'vision_patch.html#medpatchdataloader',
355
387
  'fastMONAI/vision_patch.py'),
388
+ 'fastMONAI.vision_patch.MedPatchDataLoader.__del__': ( 'vision_patch.html#medpatchdataloader.__del__',
389
+ 'fastMONAI/vision_patch.py'),
390
+ 'fastMONAI.vision_patch.MedPatchDataLoader.__enter__': ( 'vision_patch.html#medpatchdataloader.__enter__',
391
+ 'fastMONAI/vision_patch.py'),
392
+ 'fastMONAI.vision_patch.MedPatchDataLoader.__exit__': ( 'vision_patch.html#medpatchdataloader.__exit__',
393
+ 'fastMONAI/vision_patch.py'),
356
394
  'fastMONAI.vision_patch.MedPatchDataLoader.__init__': ( 'vision_patch.html#medpatchdataloader.__init__',
357
395
  'fastMONAI/vision_patch.py'),
358
396
  'fastMONAI.vision_patch.MedPatchDataLoader.__iter__': ( 'vision_patch.html#medpatchdataloader.__iter__',
359
397
  'fastMONAI/vision_patch.py'),
360
398
  'fastMONAI.vision_patch.MedPatchDataLoader.__len__': ( 'vision_patch.html#medpatchdataloader.__len__',
361
399
  'fastMONAI/vision_patch.py'),
400
+ 'fastMONAI.vision_patch.MedPatchDataLoader.close': ( 'vision_patch.html#medpatchdataloader.close',
401
+ 'fastMONAI/vision_patch.py'),
362
402
  'fastMONAI.vision_patch.MedPatchDataLoader.dataset': ( 'vision_patch.html#medpatchdataloader.dataset',
363
403
  'fastMONAI/vision_patch.py'),
364
404
  'fastMONAI.vision_patch.MedPatchDataLoader.device': ( 'vision_patch.html#medpatchdataloader.device',
@@ -369,6 +409,12 @@ d = { 'settings': { 'branch': 'main',
369
409
  'fastMONAI/vision_patch.py'),
370
410
  'fastMONAI.vision_patch.MedPatchDataLoaders': ( 'vision_patch.html#medpatchdataloaders',
371
411
  'fastMONAI/vision_patch.py'),
412
+ 'fastMONAI.vision_patch.MedPatchDataLoaders.__del__': ( 'vision_patch.html#medpatchdataloaders.__del__',
413
+ 'fastMONAI/vision_patch.py'),
414
+ 'fastMONAI.vision_patch.MedPatchDataLoaders.__enter__': ( 'vision_patch.html#medpatchdataloaders.__enter__',
415
+ 'fastMONAI/vision_patch.py'),
416
+ 'fastMONAI.vision_patch.MedPatchDataLoaders.__exit__': ( 'vision_patch.html#medpatchdataloaders.__exit__',
417
+ 'fastMONAI/vision_patch.py'),
372
418
  'fastMONAI.vision_patch.MedPatchDataLoaders.__getitem__': ( 'vision_patch.html#medpatchdataloaders.__getitem__',
373
419
  'fastMONAI/vision_patch.py'),
374
420
  'fastMONAI.vision_patch.MedPatchDataLoaders.__init__': ( 'vision_patch.html#medpatchdataloaders.__init__',
@@ -381,6 +427,8 @@ d = { 'settings': { 'branch': 'main',
381
427
  'fastMONAI/vision_patch.py'),
382
428
  'fastMONAI.vision_patch.MedPatchDataLoaders.bs': ( 'vision_patch.html#medpatchdataloaders.bs',
383
429
  'fastMONAI/vision_patch.py'),
430
+ 'fastMONAI.vision_patch.MedPatchDataLoaders.close': ( 'vision_patch.html#medpatchdataloaders.close',
431
+ 'fastMONAI/vision_patch.py'),
384
432
  'fastMONAI.vision_patch.MedPatchDataLoaders.cpu': ( 'vision_patch.html#medpatchdataloaders.cpu',
385
433
  'fastMONAI/vision_patch.py'),
386
434
  'fastMONAI.vision_patch.MedPatchDataLoaders.cuda': ( 'vision_patch.html#medpatchdataloaders.cuda',
@@ -411,6 +459,8 @@ d = { 'settings': { 'branch': 'main',
411
459
  'fastMONAI/vision_patch.py'),
412
460
  'fastMONAI.vision_patch.PatchConfig.__post_init__': ( 'vision_patch.html#patchconfig.__post_init__',
413
461
  'fastMONAI/vision_patch.py'),
462
+ 'fastMONAI.vision_patch.PatchConfig.from_dataset': ( 'vision_patch.html#patchconfig.from_dataset',
463
+ 'fastMONAI/vision_patch.py'),
414
464
  'fastMONAI.vision_patch.PatchInferenceEngine': ( 'vision_patch.html#patchinferenceengine',
415
465
  'fastMONAI/vision_patch.py'),
416
466
  'fastMONAI.vision_patch.PatchInferenceEngine.__init__': ( 'vision_patch.html#patchinferenceengine.__init__',
@@ -441,5 +491,7 @@ d = { 'settings': { 'branch': 'main',
441
491
  'fastMONAI.vision_plot.find_max_slice': ( 'vision_plot.html#find_max_slice',
442
492
  'fastMONAI/vision_plot.py'),
443
493
  'fastMONAI.vision_plot.show_med_img': ('vision_plot.html#show_med_img', 'fastMONAI/vision_plot.py'),
494
+ 'fastMONAI.vision_plot.show_segmentation_comparison': ( 'vision_plot.html#show_segmentation_comparison',
495
+ 'fastMONAI/vision_plot.py'),
444
496
  'fastMONAI.vision_plot.validate_anatomical_plane': ( 'vision_plot.html#validate_anatomical_plane',
445
497
  'fastMONAI/vision_plot.py')}}}
fastMONAI/dataset_info.py CHANGED
@@ -1,7 +1,7 @@
1
1
  # AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/08_dataset_info.ipynb.
2
2
 
3
3
  # %% auto 0
4
- __all__ = ['MedDataset', 'get_class_weights']
4
+ __all__ = ['MedDataset', 'suggest_patch_size', 'get_class_weights']
5
5
 
6
6
  # %% ../nbs/08_dataset_info.ipynb 2
7
7
  from .vision_core import *
@@ -16,11 +16,13 @@ import glob
16
16
  import matplotlib.pyplot as plt
17
17
 
18
18
  # %% ../nbs/08_dataset_info.ipynb 3
19
+ import warnings
20
+
19
21
  class MedDataset:
20
22
  """A class to extract and present information about the dataset."""
21
23
 
22
24
  def __init__(self, dataframe=None, image_col:str=None, mask_col:str="mask_path",
23
- path=None, img_list=None, postfix:str='', apply_reorder:bool=False,
25
+ path=None, img_list=None, postfix:str='', apply_reorder:bool=True,
24
26
  dtype:(MedImage, MedMask)=MedImage, max_workers:int=1):
25
27
  """Constructs MedDataset object.
26
28
 
@@ -92,14 +94,31 @@ class MedDataset:
92
94
  example_path=('path', 'min'), total=('path', 'size')
93
95
  ).sort_values('total', ascending=False)
94
96
 
95
- def get_suggestion(self):
97
+ def get_suggestion(self, include_patch_size: bool = False):
96
98
  """Returns suggested preprocessing parameters as a dictionary.
97
99
 
100
+ The returned target_spacing is derived from the mode (most common value)
101
+ of voxel spacings in the dataset.
102
+
103
+ Note:
104
+ apply_reorder is NOT included in the return value because it is not
105
+ data-derived. Access dataset.apply_reorder directly if needed.
106
+
107
+ Args:
108
+ include_patch_size: If True, includes suggested patch_size for
109
+ patch-based training. Requires vision_patch module.
110
+
98
111
  Returns:
99
- dict: {'target_spacing': [voxel_0, voxel_1, voxel_2], 'apply_reorder': bool}
112
+ dict: {'target_spacing': [voxel_0, voxel_1, voxel_2]}
113
+ If include_patch_size=True, also includes 'patch_size': [dim_0, dim_1, dim_2]
100
114
  """
101
115
  target_spacing = [float(self.df.voxel_0.mode()[0]), float(self.df.voxel_1.mode()[0]), float(self.df.voxel_2.mode()[0])]
102
- return {'target_spacing': target_spacing, 'apply_reorder': self.apply_reorder}
116
+ result = {'target_spacing': target_spacing}
117
+
118
+ if include_patch_size:
119
+ result['patch_size'] = suggest_patch_size(self)
120
+
121
+ return result
103
122
 
104
123
  def _get_data_info(self, fn: str):
105
124
  """Private method to collect information about an image file."""
@@ -133,6 +152,10 @@ class MedDataset:
133
152
  def calculate_target_size(self, target_spacing: list = None) -> list:
134
153
  """Calculate the target image size for the dataset.
135
154
 
155
+ .. deprecated::
156
+ Use `get_size_statistics(target_spacing)['max']` instead for consistency
157
+ with other size statistics methods.
158
+
136
159
  Args:
137
160
  target_spacing: If provided, calculates size after resampling to this spacing.
138
161
  If None, returns original dimensions.
@@ -140,6 +163,12 @@ class MedDataset:
140
163
  Returns:
141
164
  list: [dim_0, dim_1, dim_2] largest dimensions in dataset.
142
165
  """
166
+ warnings.warn(
167
+ "calculate_target_size() is deprecated. "
168
+ "Use get_size_statistics(target_spacing)['max'] instead.",
169
+ DeprecationWarning,
170
+ stacklevel=2
171
+ )
143
172
  if target_spacing is not None:
144
173
  org_voxels = self.df[["voxel_0", "voxel_1", 'voxel_2']].values
145
174
  org_dims = self.df[["dim_0", "dim_1", 'dim_2']].values
@@ -153,6 +182,38 @@ class MedDataset:
153
182
 
154
183
  return dims
155
184
 
185
+ def get_size_statistics(self, target_spacing: list = None) -> dict:
186
+ """Calculate comprehensive size statistics for the dataset.
187
+
188
+ Args:
189
+ target_spacing: If provided, calculates statistics after
190
+ simulating resampling to this spacing.
191
+
192
+ Returns:
193
+ dict with keys: 'median', 'min', 'max', 'std', 'percentile_10', 'percentile_90'
194
+ Each value is a list [dim_0, dim_1, dim_2].
195
+ """
196
+ if len(self.df) == 0:
197
+ raise ValueError("Dataset is empty - cannot calculate statistics")
198
+
199
+ if target_spacing is not None:
200
+ # Simulate resampled dimensions
201
+ org_voxels = self.df[["voxel_0", "voxel_1", "voxel_2"]].values
202
+ org_dims = self.df[["dim_0", "dim_1", "dim_2"]].values
203
+ ratio = org_voxels / np.array(target_spacing)
204
+ dims = np.floor(org_dims * ratio)
205
+ else:
206
+ dims = self.df[["dim_0", "dim_1", "dim_2"]].values
207
+
208
+ return {
209
+ 'median': [float(np.median(dims[:, i])) for i in range(3)],
210
+ 'min': [float(np.min(dims[:, i])) for i in range(3)],
211
+ 'max': [float(np.max(dims[:, i])) for i in range(3)],
212
+ 'std': [float(np.std(dims[:, i])) for i in range(3)],
213
+ 'percentile_10': [float(np.percentile(dims[:, i], 10)) for i in range(3)],
214
+ 'percentile_90': [float(np.percentile(dims[:, i], 90)) for i in range(3)],
215
+ }
216
+
156
217
  def get_volume_summary(self):
157
218
  """Returns DataFrame with volume statistics for each label.
158
219
 
@@ -185,7 +246,7 @@ class MedDataset:
185
246
  try:
186
247
  # Create MedImage and MedMask with current preprocessing settings
187
248
  suggestion = self.get_suggestion()
188
- MedBase.item_preprocessing(target_spacing=suggestion['target_spacing'], apply_reorder=suggestion['apply_reorder'])
249
+ MedBase.item_preprocessing(target_spacing=suggestion['target_spacing'], apply_reorder=self.apply_reorder)
189
250
 
190
251
  img = MedImage.create(img_path)
191
252
  mask = MedMask.create(mask_path)
@@ -250,7 +311,83 @@ class MedDataset:
250
311
  self._visualize_single_case(img_path, mask_path, case_id, anatomical_plane, cmap, figsize)
251
312
  print("-" * 60)
252
313
 
253
- # %% ../nbs/08_dataset_info.ipynb 5
314
+ # %% ../nbs/08_dataset_info.ipynb 4
315
+ def suggest_patch_size(
316
+ dataset: MedDataset,
317
+ target_spacing: list = None,
318
+ min_patch_size: list = None,
319
+ max_patch_size: list = None,
320
+ divisor: int = 16
321
+ ) -> list:
322
+ """Suggest optimal patch size based on median image dimensions.
323
+
324
+ Algorithm:
325
+ 1. Use median shape for robustness to outliers
326
+ 2. Round down to nearest multiple of divisor (16 for 4+ UNet pooling layers)
327
+ 3. Clamp to [min_patch_size, max_patch_size]
328
+
329
+ Args:
330
+ dataset: MedDataset instance with analyzed images.
331
+ target_spacing: Target voxel spacing [x, y, z]. If None, uses
332
+ dataset.get_suggestion()['target_spacing'].
333
+ min_patch_size: Minimum per dimension. Default [32, 32, 32].
334
+ max_patch_size: Maximum per dimension. Default [256, 256, 256].
335
+ divisor: Ensure divisibility (default 16 for UNet compatibility).
336
+
337
+ Returns:
338
+ list: [patch_dim_0, patch_dim_1, patch_dim_2]
339
+
340
+ Example:
341
+ >>> from fastMONAI.dataset_info import MedDataset
342
+ >>> dataset = MedDataset(dataframe=df, mask_col='mask_path', dtype=MedMask)
343
+ >>>
344
+ >>> # Use recommended spacing
345
+ >>> patch_size = suggest_patch_size(dataset)
346
+ >>>
347
+ >>> # Use custom spacing
348
+ >>> patch_size = suggest_patch_size(dataset, target_spacing=[1.0, 1.0, 2.0])
349
+ """
350
+ # Defaults
351
+ min_patch_size = min_patch_size or [32, 32, 32]
352
+ max_patch_size = max_patch_size or [256, 256, 256]
353
+
354
+ # Use explicit spacing or get from dataset suggestion
355
+ if target_spacing is None:
356
+ suggestion = dataset.get_suggestion()
357
+ target_spacing = suggestion['target_spacing']
358
+
359
+ # Get size statistics (resampled to target_spacing)
360
+ stats = dataset.get_size_statistics(target_spacing)
361
+ median_shape = stats['median']
362
+
363
+ # Handle single-image edge case
364
+ if len(dataset.df) == 1:
365
+ warnings.warn("Single image dataset - using image dimensions directly")
366
+
367
+ # Step 1: Round down to nearest divisor
368
+ def round_to_divisor(val, div):
369
+ """Round down to nearest multiple of divisor."""
370
+ return max(div, int(val // div) * div)
371
+
372
+ patch_size = [round_to_divisor(dim, divisor) for dim in median_shape]
373
+
374
+ # Step 2: Clamp to bounds
375
+ patch_size = [
376
+ max(min_p, min(max_p, p))
377
+ for p, min_p, max_p in zip(patch_size, min_patch_size, max_patch_size)
378
+ ]
379
+
380
+ # Edge case: image smaller than suggested patch
381
+ for i, (p, median_dim) in enumerate(zip(patch_size, median_shape)):
382
+ if median_dim < p:
383
+ warnings.warn(
384
+ f"Median dimension {i} ({median_dim:.0f}) smaller than suggested "
385
+ f"patch_size ({p}). Images will require padding."
386
+ )
387
+
388
+ return patch_size
389
+
390
+ # %% ../nbs/08_dataset_info.ipynb 6
254
391
  def get_class_weights(labels: (np.array, list), class_weight: str = 'balanced') -> torch.Tensor:
255
392
  """Calculates and returns the class weights.
256
393