fastMONAI 0.5.4__py3-none-any.whl → 0.6.1__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 +1 -1
- fastMONAI/_modidx.py +61 -1
- fastMONAI/dataset_info.py +144 -7
- fastMONAI/utils.py +296 -7
- fastMONAI/vision_augmentation.py +328 -34
- fastMONAI/vision_patch.py +175 -23
- fastMONAI/vision_plot.py +89 -1
- {fastmonai-0.5.4.dist-info → fastmonai-0.6.1.dist-info}/METADATA +1 -1
- fastmonai-0.6.1.dist-info/RECORD +21 -0
- fastmonai-0.5.4.dist-info/RECORD +0 -21
- {fastmonai-0.5.4.dist-info → fastmonai-0.6.1.dist-info}/WHEEL +0 -0
- {fastmonai-0.5.4.dist-info → fastmonai-0.6.1.dist-info}/entry_points.txt +0 -0
- {fastmonai-0.5.4.dist-info → fastmonai-0.6.1.dist-info}/licenses/LICENSE +0 -0
- {fastmonai-0.5.4.dist-info → fastmonai-0.6.1.dist-info}/top_level.txt +0 -0
fastMONAI/vision_patch.py
CHANGED
|
@@ -18,8 +18,11 @@ from typing import Callable
|
|
|
18
18
|
from torch.utils.data import DataLoader
|
|
19
19
|
from tqdm.auto import tqdm
|
|
20
20
|
from fastai.data.all import *
|
|
21
|
+
from fastai.learner import Learner
|
|
21
22
|
from .vision_core import MedImage, MedMask, MedBase, med_img_reader
|
|
22
23
|
from .vision_inference import _to_original_orientation, _do_resize
|
|
24
|
+
from .dataset_info import MedDataset, suggest_patch_size
|
|
25
|
+
from .vision_augmentation import SpatialPad
|
|
23
26
|
|
|
24
27
|
# %% ../nbs/10_vision_patch.ipynb 3
|
|
25
28
|
def _get_default_device() -> torch.device:
|
|
@@ -109,7 +112,7 @@ class PatchConfig:
|
|
|
109
112
|
queue_num_workers: Number of workers for parallel patch extraction.
|
|
110
113
|
aggregation_mode: For inference, how to combine overlapping patches ('crop', 'average', 'hann').
|
|
111
114
|
apply_reorder: Whether to reorder to RAS+ canonical orientation. Must match between
|
|
112
|
-
training and inference.
|
|
115
|
+
training and inference. Defaults to True (the common case).
|
|
113
116
|
target_spacing: Target voxel spacing [x, y, z] for resampling. Must match between
|
|
114
117
|
training and inference.
|
|
115
118
|
padding_mode: Padding mode for CropOrPad when image < patch_size. Default is 0 (zero padding)
|
|
@@ -124,7 +127,6 @@ class PatchConfig:
|
|
|
124
127
|
... samples_per_volume=16,
|
|
125
128
|
... sampler_type='label',
|
|
126
129
|
... label_probabilities={0: 0.1, 1: 0.9},
|
|
127
|
-
... apply_reorder=True,
|
|
128
130
|
... target_spacing=[0.5, 0.5, 0.5]
|
|
129
131
|
... )
|
|
130
132
|
"""
|
|
@@ -137,7 +139,7 @@ class PatchConfig:
|
|
|
137
139
|
queue_num_workers: int = 4
|
|
138
140
|
aggregation_mode: str = 'hann'
|
|
139
141
|
# Preprocessing parameters - must match between training and inference
|
|
140
|
-
apply_reorder: bool =
|
|
142
|
+
apply_reorder: bool = True # Defaults to True (the common case)
|
|
141
143
|
target_spacing: list = None
|
|
142
144
|
padding_mode: int | float | str = 0 # Zero padding (nnU-Net standard)
|
|
143
145
|
# Post-processing (binary segmentation only)
|
|
@@ -176,6 +178,74 @@ class PatchConfig:
|
|
|
176
178
|
f"Overlap >= patch_size creates step_size <= 0 (infinite patches)."
|
|
177
179
|
)
|
|
178
180
|
|
|
181
|
+
@classmethod
|
|
182
|
+
def from_dataset(
|
|
183
|
+
cls,
|
|
184
|
+
dataset: 'MedDataset',
|
|
185
|
+
target_spacing: list = None,
|
|
186
|
+
min_patch_size: list = None,
|
|
187
|
+
max_patch_size: list = None,
|
|
188
|
+
divisor: int = 16,
|
|
189
|
+
**kwargs
|
|
190
|
+
) -> 'PatchConfig':
|
|
191
|
+
"""Create PatchConfig with automatic patch_size from dataset analysis.
|
|
192
|
+
|
|
193
|
+
Combines dataset preprocessing suggestions with patch size calculation
|
|
194
|
+
for a complete, DRY configuration.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
dataset: MedDataset instance with analyzed images.
|
|
198
|
+
target_spacing: Target voxel spacing [x, y, z]. If None, uses
|
|
199
|
+
dataset.get_suggestion()['target_spacing'].
|
|
200
|
+
min_patch_size: Minimum per dimension [32, 32, 32].
|
|
201
|
+
max_patch_size: Maximum per dimension [256, 256, 256].
|
|
202
|
+
divisor: Divisibility constraint (default 16 for UNet compatibility).
|
|
203
|
+
**kwargs: Additional PatchConfig parameters (samples_per_volume,
|
|
204
|
+
sampler_type, label_probabilities, etc.).
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
PatchConfig with suggested patch_size, apply_reorder, target_spacing.
|
|
208
|
+
|
|
209
|
+
Example:
|
|
210
|
+
>>> from fastMONAI.dataset_info import MedDataset
|
|
211
|
+
>>> dataset = MedDataset(dataframe=df, mask_col='mask_path', dtype=MedMask)
|
|
212
|
+
>>>
|
|
213
|
+
>>> # Use recommended spacing
|
|
214
|
+
>>> config = PatchConfig.from_dataset(dataset, samples_per_volume=16)
|
|
215
|
+
>>>
|
|
216
|
+
>>> # Use custom spacing
|
|
217
|
+
>>> config = PatchConfig.from_dataset(
|
|
218
|
+
... dataset,
|
|
219
|
+
... target_spacing=[1.0, 1.0, 2.0],
|
|
220
|
+
... samples_per_volume=16
|
|
221
|
+
... )
|
|
222
|
+
"""
|
|
223
|
+
# Get preprocessing suggestion from dataset
|
|
224
|
+
suggestion = dataset.get_suggestion()
|
|
225
|
+
|
|
226
|
+
# Use explicit spacing or dataset suggestion
|
|
227
|
+
_target_spacing = target_spacing if target_spacing is not None else suggestion['target_spacing']
|
|
228
|
+
|
|
229
|
+
# Calculate patch size for the target spacing
|
|
230
|
+
patch_size = suggest_patch_size(
|
|
231
|
+
dataset,
|
|
232
|
+
target_spacing=_target_spacing,
|
|
233
|
+
min_patch_size=min_patch_size,
|
|
234
|
+
max_patch_size=max_patch_size,
|
|
235
|
+
divisor=divisor
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
# Merge with explicit kwargs (kwargs override defaults)
|
|
239
|
+
# Use dataset.apply_reorder directly (not from get_suggestion() since it's not data-derived)
|
|
240
|
+
config_kwargs = {
|
|
241
|
+
'patch_size': patch_size,
|
|
242
|
+
'apply_reorder': dataset.apply_reorder,
|
|
243
|
+
'target_spacing': _target_spacing,
|
|
244
|
+
}
|
|
245
|
+
config_kwargs.update(kwargs)
|
|
246
|
+
|
|
247
|
+
return cls(**config_kwargs)
|
|
248
|
+
|
|
179
249
|
# %% ../nbs/10_vision_patch.ipynb 10
|
|
180
250
|
def med_to_subject(
|
|
181
251
|
img: Path | str,
|
|
@@ -374,6 +444,9 @@ class MedPatchDataLoader:
|
|
|
374
444
|
drop_last=drop_last
|
|
375
445
|
)
|
|
376
446
|
|
|
447
|
+
# Track cleanup state
|
|
448
|
+
self._closed = False
|
|
449
|
+
|
|
377
450
|
def __iter__(self):
|
|
378
451
|
"""Iterate over batches, yielding (image, mask) tuples."""
|
|
379
452
|
for batch in self._dl:
|
|
@@ -444,6 +517,31 @@ class MedPatchDataLoader:
|
|
|
444
517
|
"""
|
|
445
518
|
return next(iter(self))
|
|
446
519
|
|
|
520
|
+
def close(self):
|
|
521
|
+
"""Shut down TorchIO Queue workers. Safe to call multiple times."""
|
|
522
|
+
if self._closed:
|
|
523
|
+
return
|
|
524
|
+
self._closed = True
|
|
525
|
+
try:
|
|
526
|
+
# Trigger cleanup of Queue's internal DataLoader iterator
|
|
527
|
+
if hasattr(self, 'queue') and hasattr(self.queue, '_subjects_iterable'):
|
|
528
|
+
self.queue._subjects_iterable = None
|
|
529
|
+
except Exception:
|
|
530
|
+
pass # Suppress cleanup errors
|
|
531
|
+
|
|
532
|
+
def __enter__(self):
|
|
533
|
+
return self
|
|
534
|
+
|
|
535
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
536
|
+
self.close()
|
|
537
|
+
return False
|
|
538
|
+
|
|
539
|
+
def __del__(self):
|
|
540
|
+
try:
|
|
541
|
+
self.close()
|
|
542
|
+
except Exception:
|
|
543
|
+
pass
|
|
544
|
+
|
|
447
545
|
# %% ../nbs/10_vision_patch.ipynb 18
|
|
448
546
|
class MedPatchDataLoaders:
|
|
449
547
|
"""fastai-compatible DataLoaders for patch-based training with LAZY loading.
|
|
@@ -454,6 +552,12 @@ class MedPatchDataLoaders:
|
|
|
454
552
|
Memory-efficient: Volumes are loaded on-demand by Queue workers,
|
|
455
553
|
keeping memory usage constant (~150 MB) regardless of dataset size.
|
|
456
554
|
|
|
555
|
+
**Automatic padding**: Images smaller than patch_size are **automatically padded**
|
|
556
|
+
using SpatialPad (zero padding, nnU-Net standard). Dimensions larger than patch_size
|
|
557
|
+
are preserved. A message is printed at DataLoader creation to inform you that
|
|
558
|
+
automatic padding is enabled. This ensures training matches inference behavior
|
|
559
|
+
where both pad small dimensions to minimum patch_size.
|
|
560
|
+
|
|
457
561
|
Note: Validation uses the same sampling as training (pseudo Dice).
|
|
458
562
|
For true validation metrics, use PatchInferenceEngine with GridSampler
|
|
459
563
|
for full-volume sliding window inference.
|
|
@@ -491,6 +595,9 @@ class MedPatchDataLoaders:
|
|
|
491
595
|
self._train_dl.to(self._device)
|
|
492
596
|
self._valid_dl.to(self._device)
|
|
493
597
|
|
|
598
|
+
# Track cleanup state
|
|
599
|
+
self._closed = False
|
|
600
|
+
|
|
494
601
|
@classmethod
|
|
495
602
|
def from_df(
|
|
496
603
|
cls,
|
|
@@ -528,6 +635,7 @@ class MedPatchDataLoaders:
|
|
|
528
635
|
target_spacing) can be set here for DRY usage with PatchInferenceEngine.
|
|
529
636
|
pre_patch_tfms: TorchIO transforms applied before patch extraction
|
|
530
637
|
(after reorder/resample). Example: [tio.ZNormalization()].
|
|
638
|
+
Accepts both fastMONAI wrappers and raw TorchIO transforms.
|
|
531
639
|
patch_tfms: TorchIO transforms applied to extracted patches (training only).
|
|
532
640
|
apply_reorder: If True, reorder to RAS+ orientation. If None, uses
|
|
533
641
|
patch_config.apply_reorder. Explicit value overrides config.
|
|
@@ -595,9 +703,19 @@ class MedPatchDataLoaders:
|
|
|
595
703
|
if _target_spacing is not None:
|
|
596
704
|
all_pre_tfms.append(tio.Resample(_target_spacing))
|
|
597
705
|
|
|
598
|
-
# Add user-provided transforms
|
|
706
|
+
# Add user-provided transforms (normalize to raw TorchIO transforms)
|
|
599
707
|
if pre_patch_tfms:
|
|
600
|
-
all_pre_tfms.extend(pre_patch_tfms)
|
|
708
|
+
all_pre_tfms.extend(normalize_patch_transforms(pre_patch_tfms))
|
|
709
|
+
|
|
710
|
+
# Add SpatialPad to ensure minimum patch_size
|
|
711
|
+
# Pads small dimensions to patch_size while preserving large dimensions.
|
|
712
|
+
# Uses zero padding (nnU-Net standard) to match inference behavior.
|
|
713
|
+
# Placed AFTER normalization ensures consistent intensity preprocessing.
|
|
714
|
+
spatial_pad = SpatialPad(spatial_size=patch_config.patch_size)
|
|
715
|
+
all_pre_tfms.append(spatial_pad.tio_transform)
|
|
716
|
+
|
|
717
|
+
# Inform user about automatic padding (transparency)
|
|
718
|
+
print(f"ℹ️ Automatic padding enabled: dimensions smaller than patch_size {patch_config.patch_size} will be padded (larger dimensions preserved)")
|
|
601
719
|
|
|
602
720
|
# Create subjects datasets with lazy loading (paths only, ~0 MB)
|
|
603
721
|
train_subjects = create_subjects_dataset(
|
|
@@ -743,9 +861,36 @@ class MedPatchDataLoaders:
|
|
|
743
861
|
def to(self, device):
|
|
744
862
|
self._device = device
|
|
745
863
|
return self
|
|
864
|
+
def cpu(self):
|
|
865
|
+
"""Move to CPU. Required for load_learner compatibility."""
|
|
866
|
+
return self.to(torch.device('cpu'))
|
|
746
867
|
|
|
747
868
|
return EmptyMedPatchDataLoaders(self._device)
|
|
748
869
|
|
|
870
|
+
def close(self):
|
|
871
|
+
"""Shut down all DataLoader workers. Safe to call multiple times."""
|
|
872
|
+
if self._closed:
|
|
873
|
+
return
|
|
874
|
+
self._closed = True
|
|
875
|
+
if hasattr(self, '_train_dl') and self._train_dl is not None:
|
|
876
|
+
self._train_dl.close()
|
|
877
|
+
if hasattr(self, '_valid_dl') and self._valid_dl is not None:
|
|
878
|
+
self._valid_dl.close()
|
|
879
|
+
|
|
880
|
+
def __enter__(self):
|
|
881
|
+
return self
|
|
882
|
+
|
|
883
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
884
|
+
self.close()
|
|
885
|
+
return False
|
|
886
|
+
|
|
887
|
+
def __del__(self):
|
|
888
|
+
try:
|
|
889
|
+
self.close()
|
|
890
|
+
except Exception:
|
|
891
|
+
pass
|
|
892
|
+
|
|
893
|
+
|
|
749
894
|
# %% ../nbs/10_vision_patch.ipynb 20
|
|
750
895
|
import numbers
|
|
751
896
|
|
|
@@ -806,7 +951,8 @@ class PatchInferenceEngine:
|
|
|
806
951
|
GridAggregator to reconstruct the full volume from predictions.
|
|
807
952
|
|
|
808
953
|
Args:
|
|
809
|
-
learner: PyTorch model
|
|
954
|
+
learner: fastai Learner or PyTorch model (nn.Module). When passing a raw
|
|
955
|
+
PyTorch model, load weights first with model.load_state_dict().
|
|
810
956
|
config: PatchConfig with inference settings. Preprocessing params (apply_reorder,
|
|
811
957
|
target_spacing, padding_mode) can be set here for DRY usage.
|
|
812
958
|
apply_reorder: Whether to reorder to RAS+ orientation. If None, uses config value.
|
|
@@ -815,21 +961,18 @@ class PatchInferenceEngine:
|
|
|
815
961
|
pre_inference_tfms: List of TorchIO transforms to apply before patch extraction.
|
|
816
962
|
IMPORTANT: Should match the pre_patch_tfms used during training (e.g., [tio.ZNormalization()]).
|
|
817
963
|
This ensures preprocessing consistency between training and inference.
|
|
964
|
+
Accepts both fastMONAI wrappers and raw TorchIO transforms.
|
|
818
965
|
|
|
819
966
|
Example:
|
|
820
|
-
>>> #
|
|
821
|
-
>>>
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
...
|
|
826
|
-
>>>
|
|
827
|
-
>>>
|
|
828
|
-
>>>
|
|
829
|
-
>>> engine = PatchInferenceEngine(
|
|
830
|
-
... learn, config,
|
|
831
|
-
... pre_inference_tfms=[tio.ZNormalization()]
|
|
832
|
-
... )
|
|
967
|
+
>>> # Option 1: From fastai Learner
|
|
968
|
+
>>> engine = PatchInferenceEngine(learn, config, pre_inference_tfms=[ZNormalization()])
|
|
969
|
+
>>> pred = engine.predict('image.nii.gz')
|
|
970
|
+
|
|
971
|
+
>>> # Option 2: From raw PyTorch model (recommended for deployment)
|
|
972
|
+
>>> model = UNet(spatial_dims=3, in_channels=1, out_channels=2, ...)
|
|
973
|
+
>>> model.load_state_dict(torch.load('weights.pth'))
|
|
974
|
+
>>> model.cuda().eval()
|
|
975
|
+
>>> engine = PatchInferenceEngine(model, config, pre_inference_tfms=[ZNormalization()])
|
|
833
976
|
>>> pred = engine.predict('image.nii.gz')
|
|
834
977
|
"""
|
|
835
978
|
|
|
@@ -845,11 +988,20 @@ class PatchInferenceEngine:
|
|
|
845
988
|
if batch_size <= 0:
|
|
846
989
|
raise ValueError(f"batch_size must be positive, got {batch_size}")
|
|
847
990
|
|
|
848
|
-
# Extract model from Learner if needed
|
|
849
|
-
|
|
991
|
+
# Extract model from Learner if needed (use isinstance for robust detection)
|
|
992
|
+
# Note: We check for Learner explicitly because some models (e.g., MONAI UNet)
|
|
993
|
+
# have a .model attribute that is NOT the full model but an internal Sequential.
|
|
994
|
+
if isinstance(learner, Learner):
|
|
995
|
+
self.model = learner.model
|
|
996
|
+
else:
|
|
997
|
+
self.model = learner # Assume it's already a PyTorch model
|
|
998
|
+
|
|
850
999
|
self.config = config
|
|
851
1000
|
self.batch_size = batch_size
|
|
852
|
-
|
|
1001
|
+
|
|
1002
|
+
# Normalize transforms to raw TorchIO (accepts both fastMONAI wrappers and raw TorchIO)
|
|
1003
|
+
normalized_tfms = normalize_patch_transforms(pre_inference_tfms)
|
|
1004
|
+
self.pre_inference_tfms = tio.Compose(normalized_tfms) if normalized_tfms else None
|
|
853
1005
|
|
|
854
1006
|
# Use config values, allow explicit overrides for backward compatibility
|
|
855
1007
|
self.apply_reorder = apply_reorder if apply_reorder is not None else config.apply_reorder
|
|
@@ -1024,7 +1176,7 @@ class PatchInferenceEngine:
|
|
|
1024
1176
|
self.model.to(device)
|
|
1025
1177
|
return self
|
|
1026
1178
|
|
|
1027
|
-
# %% ../nbs/10_vision_patch.ipynb
|
|
1179
|
+
# %% ../nbs/10_vision_patch.ipynb 22
|
|
1028
1180
|
def patch_inference(
|
|
1029
1181
|
learner,
|
|
1030
1182
|
config: PatchConfig,
|
fastMONAI/vision_plot.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/00_vision_plot.ipynb.
|
|
2
2
|
|
|
3
3
|
# %% auto 0
|
|
4
|
-
__all__ = ['validate_anatomical_plane', 'show_med_img', 'find_max_slice']
|
|
4
|
+
__all__ = ['validate_anatomical_plane', 'show_med_img', 'find_max_slice', 'show_segmentation_comparison']
|
|
5
5
|
|
|
6
6
|
# %% ../nbs/00_vision_plot.ipynb 1
|
|
7
7
|
import warnings
|
|
@@ -115,3 +115,91 @@ def find_max_slice(mask_data, anatomical_plane):
|
|
|
115
115
|
idx = np.argmax(sums)
|
|
116
116
|
|
|
117
117
|
return idx
|
|
118
|
+
|
|
119
|
+
# %% ../nbs/00_vision_plot.ipynb 7
|
|
120
|
+
def show_segmentation_comparison(
|
|
121
|
+
image, ground_truth, prediction,
|
|
122
|
+
slice_index: int = None,
|
|
123
|
+
anatomical_plane: int = 2,
|
|
124
|
+
metric_value: float = None,
|
|
125
|
+
metric_name: str = 'DSC',
|
|
126
|
+
channel: int = 0,
|
|
127
|
+
figsize: tuple = (15, 5),
|
|
128
|
+
cmap_img: str = 'gray',
|
|
129
|
+
cmap_mask: str = 'gray',
|
|
130
|
+
voxel_size = None
|
|
131
|
+
):
|
|
132
|
+
"""Display 3-panel comparison: Image | Ground Truth | Prediction.
|
|
133
|
+
|
|
134
|
+
Useful for validating segmentation results, especially after patch-based
|
|
135
|
+
inference where results are not in standard fastai batch format.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
image: Input image (MedImage, MedMask, or tensor [C, H, W, D])
|
|
139
|
+
ground_truth: Ground truth mask (MedMask or tensor [C, H, W, D])
|
|
140
|
+
prediction: Predicted mask (tensor [C, H, W, D])
|
|
141
|
+
slice_index: Slice to display. If None, uses find_max_slice on ground_truth.
|
|
142
|
+
anatomical_plane: 0=sagittal, 1=coronal, 2=axial (default)
|
|
143
|
+
metric_value: Optional metric value to display in prediction title
|
|
144
|
+
metric_name: Name of metric for title (default 'DSC')
|
|
145
|
+
channel: Channel to display for multi-channel data (default 0)
|
|
146
|
+
figsize: Figure size (default (15, 5))
|
|
147
|
+
cmap_img: Colormap for image (default 'gray')
|
|
148
|
+
cmap_mask: Colormap for masks (default 'gray')
|
|
149
|
+
voxel_size: Voxel spacing for aspect ratio. If None, aspect=1.
|
|
150
|
+
|
|
151
|
+
Example::
|
|
152
|
+
|
|
153
|
+
# After patch_inference()
|
|
154
|
+
show_segmentation_comparison(
|
|
155
|
+
image=val_img,
|
|
156
|
+
ground_truth=val_gt,
|
|
157
|
+
prediction=predictions[0],
|
|
158
|
+
metric_value=results_df.iloc[0]['DSC'],
|
|
159
|
+
anatomical_plane=2
|
|
160
|
+
)
|
|
161
|
+
"""
|
|
162
|
+
validate_anatomical_plane(anatomical_plane)
|
|
163
|
+
|
|
164
|
+
# Ensure we have tensor data
|
|
165
|
+
img_data = image.data if hasattr(image, 'data') else image
|
|
166
|
+
gt_data = ground_truth.data if hasattr(ground_truth, 'data') else ground_truth
|
|
167
|
+
pred_data = prediction.data if hasattr(prediction, 'data') else prediction
|
|
168
|
+
|
|
169
|
+
# Move to CPU if needed
|
|
170
|
+
if hasattr(img_data, 'cpu'): img_data = img_data.cpu()
|
|
171
|
+
if hasattr(gt_data, 'cpu'): gt_data = gt_data.cpu()
|
|
172
|
+
if hasattr(pred_data, 'cpu'): pred_data = pred_data.cpu()
|
|
173
|
+
|
|
174
|
+
# Find optimal slice if not provided
|
|
175
|
+
if slice_index is None:
|
|
176
|
+
gt_np = gt_data[channel].numpy()
|
|
177
|
+
slice_index = find_max_slice(gt_np, anatomical_plane)
|
|
178
|
+
|
|
179
|
+
# Create figure
|
|
180
|
+
fig, axes = plt.subplots(1, 3, figsize=figsize)
|
|
181
|
+
|
|
182
|
+
# Get slices with proper aspect ratio using existing _get_slice helper
|
|
183
|
+
img_slice, img_aspect = _get_slice(img_data, channel, slice_index, anatomical_plane, voxel_size)
|
|
184
|
+
gt_slice, gt_aspect = _get_slice(gt_data, channel, slice_index, anatomical_plane, voxel_size)
|
|
185
|
+
pred_slice, pred_aspect = _get_slice(pred_data, channel, slice_index, anatomical_plane, voxel_size)
|
|
186
|
+
|
|
187
|
+
# Plot panels
|
|
188
|
+
axes[0].imshow(img_slice, cmap=cmap_img, aspect=img_aspect)
|
|
189
|
+
axes[0].set_title('Input Image')
|
|
190
|
+
axes[0].axis('off')
|
|
191
|
+
|
|
192
|
+
axes[1].imshow(gt_slice, cmap=cmap_mask, aspect=gt_aspect)
|
|
193
|
+
axes[1].set_title('Ground Truth')
|
|
194
|
+
axes[1].axis('off')
|
|
195
|
+
|
|
196
|
+
# Build prediction title
|
|
197
|
+
pred_title = 'Prediction'
|
|
198
|
+
if metric_value is not None:
|
|
199
|
+
pred_title = f'Prediction ({metric_name}: {metric_value:.4f})'
|
|
200
|
+
|
|
201
|
+
axes[2].imshow(pred_slice, cmap=cmap_mask, aspect=pred_aspect)
|
|
202
|
+
axes[2].set_title(pred_title)
|
|
203
|
+
axes[2].axis('off')
|
|
204
|
+
|
|
205
|
+
plt.tight_layout()
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
fastMONAI/__init__.py,sha256=baAcEjLSYFIeNZF51tOMmA_zAMhN8HvKael-UU-Ruec,22
|
|
2
|
+
fastMONAI/_modidx.py,sha256=x9tW-MTHa2dTdBrQPd15CxAiBtTVMGKTkW54SCu0u6A,67370
|
|
3
|
+
fastMONAI/dataset_info.py,sha256=hLNX0Jps8dPCbF6HYPO2PScZtn2qqAQBH07LtSubeFM,16306
|
|
4
|
+
fastMONAI/external_data.py,sha256=Ofa6RmSKYj8LKlzWqPGfg9lu9lTjBmolJTo1zVgTe6g,12263
|
|
5
|
+
fastMONAI/research_utils.py,sha256=LZu62g8BQAVYS4dD7qDsKHJXZnDd1uLkJ6LoaMDhUhk,590
|
|
6
|
+
fastMONAI/utils.py,sha256=8avga5wIGK4IlJ04SrN_Ba5STkBSuEA5G06EvdDHNBs,30493
|
|
7
|
+
fastMONAI/vision_all.py,sha256=L2JVYQq77X7Ko2SmzKUGAXwVjw898V_skGuQuYxjVd8,385
|
|
8
|
+
fastMONAI/vision_augmentation.py,sha256=ym7scsWCRQ5Zh7MGwK653IFWyhEYYGgggJkwR4mhOCA,27276
|
|
9
|
+
fastMONAI/vision_core.py,sha256=xXGnutYD3sdWzpoMZHSfClUFmphsB7yFsDcY91Fa844,9539
|
|
10
|
+
fastMONAI/vision_data.py,sha256=_SgwSlNm4ZOooFnrp5vYnA7ZAweV60a3XaOnozDKm6w,11569
|
|
11
|
+
fastMONAI/vision_inference.py,sha256=fRPgIO-3XifBlCZY7qaucgymSLQQajcKvmZKWg0XfS4,7688
|
|
12
|
+
fastMONAI/vision_loss.py,sha256=NrHnk1yD4EBKsp6aippppXU4l-mwmsZOqE_bsZP3ZNI,3591
|
|
13
|
+
fastMONAI/vision_metrics.py,sha256=OED2ewWaZUtmkqc5CjmBWDboTVUlpsnsuFU-ZrIw4tI,18572
|
|
14
|
+
fastMONAI/vision_patch.py,sha256=1TFDGPL0SiJM9vIS217mMgChqVtnipuJQ7xoXM-3nAU,51204
|
|
15
|
+
fastMONAI/vision_plot.py,sha256=7HLkKQAwmwPvwrDxWlKP5IIHNNwWIrLkdsejqlOc27k,7282
|
|
16
|
+
fastmonai-0.6.1.dist-info/licenses/LICENSE,sha256=xV8xoN4VOL0uw9X8RSs2IMuD_Ss_a9yAbtGNeBWZwnw,11337
|
|
17
|
+
fastmonai-0.6.1.dist-info/METADATA,sha256=qP179D1B953PnbEo3ee9EufYLZxKv7nuqeHNSDC6fOQ,7075
|
|
18
|
+
fastmonai-0.6.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
19
|
+
fastmonai-0.6.1.dist-info/entry_points.txt,sha256=mVBsykSXMairzzk3hJaQ8c-UiwUZqGnn4aFZ24CpsBM,40
|
|
20
|
+
fastmonai-0.6.1.dist-info/top_level.txt,sha256=o8y7SWF9odtnIT3jvYtUn9okbJRlaAMCy7oPFCeQvQ8,10
|
|
21
|
+
fastmonai-0.6.1.dist-info/RECORD,,
|
fastmonai-0.5.4.dist-info/RECORD
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
fastMONAI/__init__.py,sha256=DITpct-LrdIsTgwx2NgH5Ghx5y8Xgz1YMimy1ZV5RTY,22
|
|
2
|
-
fastMONAI/_modidx.py,sha256=eEccmLX_gTevLLfGe2ywec46xRkZrduEndx1lDMEync,59028
|
|
3
|
-
fastMONAI/dataset_info.py,sha256=BzbTZAGTQ5idyXqf-tna84No5tu9Fjo2RJA6lTqhaW8,11043
|
|
4
|
-
fastMONAI/external_data.py,sha256=Ofa6RmSKYj8LKlzWqPGfg9lu9lTjBmolJTo1zVgTe6g,12263
|
|
5
|
-
fastMONAI/research_utils.py,sha256=LZu62g8BQAVYS4dD7qDsKHJXZnDd1uLkJ6LoaMDhUhk,590
|
|
6
|
-
fastMONAI/utils.py,sha256=6bUgyVzUenkAUtf9gxJXeRsz2xc8uzjqsvzU2UVDKNI,19944
|
|
7
|
-
fastMONAI/vision_all.py,sha256=L2JVYQq77X7Ko2SmzKUGAXwVjw898V_skGuQuYxjVd8,385
|
|
8
|
-
fastMONAI/vision_augmentation.py,sha256=EFEUkpU7xaGJGZd6iCqOfDe56zVrgAV8t_UC0iAAum0,15611
|
|
9
|
-
fastMONAI/vision_core.py,sha256=xXGnutYD3sdWzpoMZHSfClUFmphsB7yFsDcY91Fa844,9539
|
|
10
|
-
fastMONAI/vision_data.py,sha256=_SgwSlNm4ZOooFnrp5vYnA7ZAweV60a3XaOnozDKm6w,11569
|
|
11
|
-
fastMONAI/vision_inference.py,sha256=fRPgIO-3XifBlCZY7qaucgymSLQQajcKvmZKWg0XfS4,7688
|
|
12
|
-
fastMONAI/vision_loss.py,sha256=NrHnk1yD4EBKsp6aippppXU4l-mwmsZOqE_bsZP3ZNI,3591
|
|
13
|
-
fastMONAI/vision_metrics.py,sha256=OED2ewWaZUtmkqc5CjmBWDboTVUlpsnsuFU-ZrIw4tI,18572
|
|
14
|
-
fastMONAI/vision_patch.py,sha256=C-LQEYt1bYCdmWG9PZJdCqlB1ucqNEA40qVF5gDA_Bs,44927
|
|
15
|
-
fastMONAI/vision_plot.py,sha256=hdQQtS2fRdImKQABaUtK0vpmRw4hHEE8oZnsKAGy-0o,3808
|
|
16
|
-
fastmonai-0.5.4.dist-info/licenses/LICENSE,sha256=xV8xoN4VOL0uw9X8RSs2IMuD_Ss_a9yAbtGNeBWZwnw,11337
|
|
17
|
-
fastmonai-0.5.4.dist-info/METADATA,sha256=ssu77BruDlG-RdrjO2ee7x43WzbPN1WSytpmLgkd4YE,7075
|
|
18
|
-
fastmonai-0.5.4.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
19
|
-
fastmonai-0.5.4.dist-info/entry_points.txt,sha256=mVBsykSXMairzzk3hJaQ8c-UiwUZqGnn4aFZ24CpsBM,40
|
|
20
|
-
fastmonai-0.5.4.dist-info/top_level.txt,sha256=o8y7SWF9odtnIT3jvYtUn9okbJRlaAMCy7oPFCeQvQ8,10
|
|
21
|
-
fastmonai-0.5.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|