monai-weekly 1.5.dev2451__py3-none-any.whl → 1.5.dev2501__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.
monai/__init__.py CHANGED
@@ -136,4 +136,4 @@ except BaseException:
136
136
 
137
137
  if MONAIEnvVars.debug():
138
138
  raise
139
- __commit_id__ = "efff647a332f9520e7b7d7565893bd16ab26e041"
139
+ __commit_id__ = "996e876e7542f683508aa04e74b97e284bbde72b"
monai/_version.py CHANGED
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2024-12-22T02:28:23+0000",
11
+ "date": "2025-01-05T02:28:29+0000",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "7c1c58cd10db72c01b5cdda1600cd68e262437cf",
15
- "version": "1.5.dev2451"
14
+ "full-revisionid": "5a5531d74a1a2cb3f0f3bbe16f5d445ca4639456",
15
+ "version": "1.5.dev2501"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -12,8 +12,11 @@
12
12
  from __future__ import annotations
13
13
 
14
14
  import glob
15
+ import gzip
16
+ import io
15
17
  import os
16
18
  import re
19
+ import tempfile
17
20
  import warnings
18
21
  from abc import ABC, abstractmethod
19
22
  from collections.abc import Callable, Iterable, Iterator, Sequence
@@ -51,6 +54,9 @@ else:
51
54
  pydicom, has_pydicom = optional_import("pydicom")
52
55
  nrrd, has_nrrd = optional_import("nrrd", allow_namespace_pkg=True)
53
56
 
57
+ cp, has_cp = optional_import("cupy")
58
+ kvikio, has_kvikio = optional_import("kvikio")
59
+
54
60
  __all__ = ["ImageReader", "ITKReader", "NibabelReader", "NumpyReader", "PILReader", "PydicomReader", "NrrdReader"]
55
61
 
56
62
 
@@ -137,14 +143,18 @@ def _copy_compatible_dict(from_dict: dict, to_dict: dict):
137
143
  )
138
144
 
139
145
 
140
- def _stack_images(image_list: list, meta_dict: dict):
146
+ def _stack_images(image_list: list, meta_dict: dict, to_cupy: bool = False):
141
147
  if len(image_list) <= 1:
142
148
  return image_list[0]
143
149
  if not is_no_channel(meta_dict.get(MetaKeys.ORIGINAL_CHANNEL_DIM, None)):
144
150
  channel_dim = int(meta_dict[MetaKeys.ORIGINAL_CHANNEL_DIM])
151
+ if to_cupy and has_cp:
152
+ return cp.concatenate(image_list, axis=channel_dim)
145
153
  return np.concatenate(image_list, axis=channel_dim)
146
154
  # stack at a new first dim as the channel dim, if `'original_channel_dim'` is unspecified
147
155
  meta_dict[MetaKeys.ORIGINAL_CHANNEL_DIM] = 0
156
+ if to_cupy and has_cp:
157
+ return cp.stack(image_list, axis=0)
148
158
  return np.stack(image_list, axis=0)
149
159
 
150
160
 
@@ -864,12 +874,18 @@ class NibabelReader(ImageReader):
864
874
  Load NIfTI format images based on Nibabel library.
865
875
 
866
876
  Args:
867
- as_closest_canonical: if True, load the image as closest to canonical axis format.
868
- squeeze_non_spatial_dims: if True, non-spatial singletons will be squeezed, e.g. (256,256,1,3) -> (256,256,3)
869
877
  channel_dim: the channel dimension of the input image, default is None.
870
878
  this is used to set original_channel_dim in the metadata, EnsureChannelFirstD reads this field.
871
879
  if None, `original_channel_dim` will be either `no_channel` or `-1`.
872
880
  most Nifti files are usually "channel last", no need to specify this argument for them.
881
+ as_closest_canonical: if True, load the image as closest to canonical axis format.
882
+ squeeze_non_spatial_dims: if True, non-spatial singletons will be squeezed, e.g. (256,256,1,3) -> (256,256,3)
883
+ to_gpu: If True, load the image into GPU memory using CuPy and Kvikio. This can accelerate data loading.
884
+ Default is False. CuPy and Kvikio are required for this option.
885
+ Note: For compressed NIfTI files, some operations may still be performed on CPU memory,
886
+ and the acceleration may not be significant. In some cases, it may be slower than loading on CPU.
887
+ In practical use, it's recommended to add a warm up call before the actual loading.
888
+ A related tutorial will be prepared in the future, and the document will be updated accordingly.
873
889
  kwargs: additional args for `nibabel.load` API. more details about available args:
874
890
  https://github.com/nipy/nibabel/blob/master/nibabel/loadsave.py
875
891
 
@@ -880,14 +896,42 @@ class NibabelReader(ImageReader):
880
896
  channel_dim: str | int | None = None,
881
897
  as_closest_canonical: bool = False,
882
898
  squeeze_non_spatial_dims: bool = False,
899
+ to_gpu: bool = False,
883
900
  **kwargs,
884
901
  ):
885
902
  super().__init__()
886
903
  self.channel_dim = float("nan") if channel_dim == "no_channel" else channel_dim
887
904
  self.as_closest_canonical = as_closest_canonical
888
905
  self.squeeze_non_spatial_dims = squeeze_non_spatial_dims
906
+ if to_gpu and (not has_cp or not has_kvikio):
907
+ warnings.warn(
908
+ "NibabelReader: CuPy and/or Kvikio not installed for GPU loading, falling back to CPU loading."
909
+ )
910
+ to_gpu = False
911
+
912
+ if to_gpu:
913
+ self.warmup_kvikio()
914
+
915
+ self.to_gpu = to_gpu
889
916
  self.kwargs = kwargs
890
917
 
918
+ def warmup_kvikio(self):
919
+ """
920
+ Warm up the Kvikio library to initialize the internal buffers, cuFile, GDS, etc.
921
+ This can accelerate the data loading process when `to_gpu` is set to True.
922
+ """
923
+ if has_cp and has_kvikio:
924
+ a = cp.arange(100)
925
+ with tempfile.NamedTemporaryFile() as tmp_file:
926
+ tmp_file_name = tmp_file.name
927
+ f = kvikio.CuFile(tmp_file_name, "w")
928
+ f.write(a)
929
+ f.close()
930
+
931
+ b = cp.empty_like(a)
932
+ f = kvikio.CuFile(tmp_file_name, "r")
933
+ f.read(b)
934
+
891
935
  def verify_suffix(self, filename: Sequence[PathLike] | PathLike) -> bool:
892
936
  """
893
937
  Verify whether the specified file or files format is supported by Nibabel reader.
@@ -916,6 +960,7 @@ class NibabelReader(ImageReader):
916
960
  img_: list[Nifti1Image] = []
917
961
 
918
962
  filenames: Sequence[PathLike] = ensure_tuple(data)
963
+ self.filenames = filenames
919
964
  kwargs_ = self.kwargs.copy()
920
965
  kwargs_.update(kwargs)
921
966
  for name in filenames:
@@ -936,10 +981,13 @@ class NibabelReader(ImageReader):
936
981
  img: a Nibabel image object loaded from an image file or a list of Nibabel image objects.
937
982
 
938
983
  """
984
+ # TODO: the actual type is list[np.ndarray | cp.ndarray]
985
+ # should figure out how to define correct types without having cupy not found error
986
+ # https://github.com/Project-MONAI/MONAI/pull/8188#discussion_r1886645918
939
987
  img_array: list[np.ndarray] = []
940
988
  compatible_meta: dict = {}
941
989
 
942
- for i in ensure_tuple(img):
990
+ for i, filename in zip(ensure_tuple(img), self.filenames):
943
991
  header = self._get_meta_dict(i)
944
992
  header[MetaKeys.AFFINE] = self._get_affine(i)
945
993
  header[MetaKeys.ORIGINAL_AFFINE] = self._get_affine(i)
@@ -949,7 +997,7 @@ class NibabelReader(ImageReader):
949
997
  header[MetaKeys.AFFINE] = self._get_affine(i)
950
998
  header[MetaKeys.SPATIAL_SHAPE] = self._get_spatial_shape(i)
951
999
  header[MetaKeys.SPACE] = SpaceKeys.RAS
952
- data = self._get_array_data(i)
1000
+ data = self._get_array_data(i, filename)
953
1001
  if self.squeeze_non_spatial_dims:
954
1002
  for d in range(len(data.shape), len(header[MetaKeys.SPATIAL_SHAPE]), -1):
955
1003
  if data.shape[d - 1] == 1:
@@ -963,7 +1011,7 @@ class NibabelReader(ImageReader):
963
1011
  header[MetaKeys.ORIGINAL_CHANNEL_DIM] = self.channel_dim
964
1012
  _copy_compatible_dict(header, compatible_meta)
965
1013
 
966
- return _stack_images(img_array, compatible_meta), compatible_meta
1014
+ return _stack_images(img_array, compatible_meta, to_cupy=self.to_gpu), compatible_meta
967
1015
 
968
1016
  def _get_meta_dict(self, img) -> dict:
969
1017
  """
@@ -1015,14 +1063,34 @@ class NibabelReader(ImageReader):
1015
1063
  spatial_rank = max(min(ndim, 3), 1)
1016
1064
  return np.asarray(size[:spatial_rank])
1017
1065
 
1018
- def _get_array_data(self, img):
1066
+ def _get_array_data(self, img, filename):
1019
1067
  """
1020
1068
  Get the raw array data of the image, converted to Numpy array.
1021
1069
 
1022
1070
  Args:
1023
1071
  img: a Nibabel image object loaded from an image file.
1024
-
1025
- """
1072
+ filename: file name of the image.
1073
+
1074
+ """
1075
+ if self.to_gpu:
1076
+ file_size = os.path.getsize(filename)
1077
+ image = cp.empty(file_size, dtype=cp.uint8)
1078
+ with kvikio.CuFile(filename, "r") as f:
1079
+ f.read(image)
1080
+ if filename.endswith(".nii.gz"):
1081
+ # for compressed data, have to tansfer to CPU to decompress
1082
+ # and then transfer back to GPU. It is not efficient compared to .nii file
1083
+ # and may be slower than CPU loading in some cases.
1084
+ warnings.warn("Loading compressed NIfTI file into GPU may not be efficient.")
1085
+ compressed_data = cp.asnumpy(image)
1086
+ with gzip.GzipFile(fileobj=io.BytesIO(compressed_data)) as gz_file:
1087
+ decompressed_data = gz_file.read()
1088
+
1089
+ image = cp.frombuffer(decompressed_data, dtype=cp.uint8)
1090
+ data_shape = img.shape
1091
+ data_offset = img.dataobj.offset
1092
+ data_dtype = img.dataobj.dtype
1093
+ return image[data_offset:].view(data_dtype).reshape(data_shape, order="F")
1026
1094
  return np.asanyarray(img.dataobj, order="C")
1027
1095
 
1028
1096
 
monai/data/meta_tensor.py CHANGED
@@ -553,7 +553,6 @@ class MetaTensor(MetaObj, torch.Tensor):
553
553
  However, if `get_track_meta()` is `False` or meta=None, a `torch.Tensor` is returned.
554
554
  """
555
555
  img = convert_to_tensor(im, track_meta=get_track_meta() and meta is not None) # potentially ascontiguousarray
556
-
557
556
  # if not tracking metadata, return `torch.Tensor`
558
557
  if not isinstance(img, MetaTensor):
559
558
  return img
@@ -286,7 +286,6 @@ class LoadImage(Transform):
286
286
  " https://docs.monai.io/en/latest/installation.html#installing-the-recommended-dependencies.\n"
287
287
  f" The current registered: {self.readers}.\n{msg}"
288
288
  )
289
-
290
289
  img_array: NdarrayOrTensor
291
290
  img_array, meta_data = reader.get_data(img)
292
291
  img_array = convert_to_dst_type(img_array, dst=img_array, dtype=self.dtype)[0]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: monai-weekly
3
- Version: 1.5.dev2451
3
+ Version: 1.5.dev2501
4
4
  Summary: AI Toolkit for Healthcare Imaging
5
5
  Home-page: https://monai.io/
6
6
  Author: MONAI Consortium
@@ -1,5 +1,5 @@
1
- monai/__init__.py,sha256=9uBqEYRgHOLxHMXL6KaeW64h3EEBWzVc8Q5_qNdvbHw,4095
2
- monai/_version.py,sha256=xt33zPGBBDISzFAylvD8rM8SeH-5WwgMQzCJR1hckIE,503
1
+ monai/__init__.py,sha256=EDmpj3WGm6pqQLjdXAYmCYvFgRTzMlBZ9vsUlJF2-H8,4095
2
+ monai/_version.py,sha256=hq5lqkY7TJcQbePDU-i2WF2tRziUTMy7_5pXWu8u1IA,503
3
3
  monai/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  monai/_extensions/__init__.py,sha256=NEBPreRhQ8H9gVvgrLr_y52_TmqB96u_u4VQmeNT93I,642
5
5
  monai/_extensions/loader.py,sha256=7SiKw36q-nOzH8CRbBurFrz7GM40GCu7rc93Tm8XpnI,3643
@@ -131,12 +131,12 @@ monai/data/fft_utils.py,sha256=in9Zu8hC4oSVzuA-Zl236X6EkvgFka0RXdOxgvdGkv0,4448
131
131
  monai/data/folder_layout.py,sha256=IsHW1-Bkupn_T8r6MgFTIJQh5HwCg0xQwOKmgBtl0gE,6344
132
132
  monai/data/grid_dataset.py,sha256=O0gHf3BgrisH1erOMZNSpoIut92mydnNpzGYWnBKg4U,19483
133
133
  monai/data/image_dataset.py,sha256=OhNJ3awauWtqsDhefIGDw3UYGF3RoleeNUPdJOKg3kI,7008
134
- monai/data/image_reader.py,sha256=XDkYVWQN_eHoMI1iFFWN8ICI0x9AxKSc8bGSavHskfs,61776
134
+ monai/data/image_reader.py,sha256=5Hd0rSkK7D-dWt6LENS2AJ8BZqkPLrlc0jdxRMOWGUM,65114
135
135
  monai/data/image_writer.py,sha256=rH6vboPFkX4ziN3lnrmK6AzAOQYI9tEiOJb7Al2tj-8,39856
136
136
  monai/data/iterable_dataset.py,sha256=A0L5jaxwnfgProBj96tlT160esI21yutnTf3a4c29Ms,13100
137
137
  monai/data/itk_torch_bridge.py,sha256=3th-B3tJuJE22JFfOUgGeTMOPh1czJEiSccFyn_Ob0w,14461
138
138
  monai/data/meta_obj.py,sha256=EDQdYbc4HkHcuDYbokvuIbDW-peqvnPW2JPWT8zgaNU,8827
139
- monai/data/meta_tensor.py,sha256=LCyAhyjZNh4ynFt2EPZFsKcledIXMDO-XFsx_VBom5I,27557
139
+ monai/data/meta_tensor.py,sha256=iK3er8zkj88O4XxTOBO2slhuwxBKuM-3Xh_HUejxo2I,27556
140
140
  monai/data/samplers.py,sha256=LUCAHy38ddGm67oJJp3W6ITBsDRqyGCrKtYn-pjrWc4,5102
141
141
  monai/data/synthetic.py,sha256=H0MaQq2nnYxXEMlvOW1-XoWJWY_VKsgZ75tWLO1aCXg,7375
142
142
  monai/data/test_time_augmentation.py,sha256=KgIcPDwF_KelBCX118J5gx13sefGaDgQFUDgGWCZujA,9871
@@ -370,7 +370,7 @@ monai/transforms/intensity/__init__.py,sha256=s9djSd6kvViPnFvMR11Dgd30Lv4oY6FaPJ
370
370
  monai/transforms/intensity/array.py,sha256=SpG3u9LPuQxDk77lEvPC4-tH1tiOtacDDfcyQydIhkI,121592
371
371
  monai/transforms/intensity/dictionary.py,sha256=MEeMKQckn6X-cEk51Z2YTGjt89RohBzFfO_jU3D06wk,85086
372
372
  monai/transforms/io/__init__.py,sha256=s9djSd6kvViPnFvMR11Dgd30Lv4oY6FaPJr4ZZJZLq0,573
373
- monai/transforms/io/array.py,sha256=z4aOxK44IhztN-LzG2uROYDwg_u1C6gcpx9ZH-ZhoVA,27482
373
+ monai/transforms/io/array.py,sha256=QxB19z8aBhaSNlSkrubyoM0swHqTcDatWT0GrzmOOwM,27481
374
374
  monai/transforms/io/dictionary.py,sha256=64M9KUsKyzwXopDcarXT7JKIv9rHP8Ae-fYRvI0yBuM,18716
375
375
  monai/transforms/lazy/__init__.py,sha256=s9djSd6kvViPnFvMR11Dgd30Lv4oY6FaPJr4ZZJZLq0,573
376
376
  monai/transforms/lazy/array.py,sha256=2jNLmQ3_sMX7DdbfcT3Extpwe5FgOBbbz2RqlDlyNcw,1211
@@ -420,8 +420,8 @@ monai/visualize/img2tensorboard.py,sha256=NnMcyfIFqX-jD7TBO3Rn02zt5uug79d_7pIIaV
420
420
  monai/visualize/occlusion_sensitivity.py,sha256=OQHEJLyIhB8zWqQsfKaX-1kvCjWFVYtLfS4dFC0nKFI,18160
421
421
  monai/visualize/utils.py,sha256=B-MhTVs7sQbIqYS3yPnpBwPw2K82rE2PBtGIfpwZtWM,9894
422
422
  monai/visualize/visualizer.py,sha256=qckyaMZCbezYUwE20k5yc-Pb7UozVavMDbrmyQwfYHY,1377
423
- monai_weekly-1.5.dev2451.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
424
- monai_weekly-1.5.dev2451.dist-info/METADATA,sha256=0Tg9NUHnzUVF_3-QgVzTfxt0yaQQ1X39LiDjn1THmws,11876
425
- monai_weekly-1.5.dev2451.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
426
- monai_weekly-1.5.dev2451.dist-info/top_level.txt,sha256=UaNwRzLGORdus41Ip446s3bBfViLkdkDsXDo34J2P44,6
427
- monai_weekly-1.5.dev2451.dist-info/RECORD,,
423
+ monai_weekly-1.5.dev2501.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
424
+ monai_weekly-1.5.dev2501.dist-info/METADATA,sha256=lPpVp6DVewBS_b3g83G0xSKcSOpVE74pHs5ZwjlCmzg,11876
425
+ monai_weekly-1.5.dev2501.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
426
+ monai_weekly-1.5.dev2501.dist-info/top_level.txt,sha256=UaNwRzLGORdus41Ip446s3bBfViLkdkDsXDo34J2P44,6
427
+ monai_weekly-1.5.dev2501.dist-info/RECORD,,