junifer 0.0.6.dev366__py3-none-any.whl → 0.0.6.dev378__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.
junifer/_version.py CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.0.6.dev366'
16
- __version_tuple__ = version_tuple = (0, 0, 6, 'dev366')
15
+ __version__ = version = '0.0.6.dev378'
16
+ __version_tuple__ = version_tuple = (0, 0, 6, 'dev378')
@@ -7,6 +7,7 @@ import uuid
7
7
  from typing import TYPE_CHECKING, Any, Optional
8
8
 
9
9
  import nibabel as nib
10
+ import numpy as np
10
11
 
11
12
  from ...pipeline import WorkDirManager
12
13
  from ...utils import logger, raise_error, run_ext_cmd
@@ -20,6 +21,26 @@ if TYPE_CHECKING:
20
21
  __all__ = ["ANTsMaskWarper"]
21
22
 
22
23
 
24
+ def _get_interpolation_method(img: "Nifti1Image") -> str:
25
+ """Get correct interpolation method for `img`.
26
+
27
+ Parameters
28
+ ----------
29
+ img : nibabel.nifti1.Nifti1Image
30
+ The image.
31
+
32
+ Returns
33
+ -------
34
+ str
35
+ The interpolation method.
36
+
37
+ """
38
+ if np.array_equal(np.unique(img.get_fdata()), [0, 1]):
39
+ return "'GenericLabel[NearestNeighbor]'"
40
+ else:
41
+ return "LanczosWindowedSinc"
42
+
43
+
23
44
  class ANTsMaskWarper:
24
45
  """Class for mask space warping via ANTs.
25
46
 
@@ -143,7 +164,7 @@ class ANTsMaskWarper:
143
164
  "antsApplyTransforms",
144
165
  "-d 3",
145
166
  "-e 3",
146
- "-n 'GenericLabel[NearestNeighbor]'",
167
+ f"-n {_get_interpolation_method(mask_img)}",
147
168
  f"-i {prewarp_mask_path.resolve()}",
148
169
  f"-r {template_space_img_path.resolve()}",
149
170
  f"-t {xfm_file_path.resolve()}",
@@ -7,6 +7,7 @@ import uuid
7
7
  from typing import TYPE_CHECKING, Any
8
8
 
9
9
  import nibabel as nib
10
+ import numpy as np
10
11
 
11
12
  from ...pipeline import WorkDirManager
12
13
  from ...utils import logger, run_ext_cmd
@@ -19,6 +20,26 @@ if TYPE_CHECKING:
19
20
  __all__ = ["FSLMaskWarper"]
20
21
 
21
22
 
23
+ def _get_interpolation_method(img: "Nifti1Image") -> str:
24
+ """Get correct interpolation method for `img`.
25
+
26
+ Parameters
27
+ ----------
28
+ img : nibabel.nifti1.Nifti1Image
29
+ The image.
30
+
31
+ Returns
32
+ -------
33
+ str
34
+ The interpolation method.
35
+
36
+ """
37
+ if np.array_equal(np.unique(img.get_fdata()), [0, 1]):
38
+ return "nn"
39
+ else:
40
+ return "spline"
41
+
42
+
22
43
  class FSLMaskWarper:
23
44
  """Class for mask space warping via FSL FLIRT.
24
45
 
@@ -71,7 +92,7 @@ class FSLMaskWarper:
71
92
  # Set applywarp command
72
93
  applywarp_cmd = [
73
94
  "applywarp",
74
- "--interp=nn",
95
+ f"--interp={_get_interpolation_method(mask_img)}",
75
96
  f"-i {prewarp_mask_path.resolve()}",
76
97
  # use resampled reference
77
98
  f"-r {target_data['reference']['path'].resolve()}",
@@ -48,6 +48,7 @@ def compute_brain_mask(
48
48
  mask_type: str = "brain",
49
49
  threshold: float = 0.5,
50
50
  source: str = "template",
51
+ template_space: Optional[str] = None,
51
52
  extra_input: Optional[dict[str, Any]] = None,
52
53
  ) -> "Nifti1Image":
53
54
  """Compute the whole-brain, grey-matter or white-matter mask.
@@ -78,6 +79,9 @@ def compute_brain_mask(
78
79
  The source of the mask. If "subject", the mask is computed from the
79
80
  subject's data (``VBM_GM`` or ``VBM_WM``). If "template", the mask is
80
81
  computed from the template data (default "template").
82
+ template_space : str, optional
83
+ The space of the template. If not provided, the space is inferred from
84
+ the ``target_data`` (default None).
81
85
  extra_input : dict, optional
82
86
  The other fields in the data object. Useful for accessing other data
83
87
  types (default None).
@@ -93,6 +97,7 @@ def compute_brain_mask(
93
97
  If ``mask_type`` is invalid or
94
98
  if ``source`` is invalid or
95
99
  if ``source="subject"`` and ``mask_type`` is invalid or
100
+ if ``template_space`` is provided when ``source="subject"`` or
96
101
  if ``warp_data`` is None when ``target_data``'s space is native or
97
102
  if ``extra_input`` is None when ``source="subject"`` or
98
103
  if ``VBM_GM`` or ``VBM_WM`` data types are not in ``extra_input``
@@ -111,6 +116,9 @@ def compute_brain_mask(
111
116
  if source == "subject" and mask_type not in ["gm", "wm"]:
112
117
  raise_error(f"Unknown mask type: {mask_type} for subject space")
113
118
 
119
+ if source == "subject" and template_space is not None:
120
+ raise_error("Cannot provide `template_space` when source is `subject`")
121
+
114
122
  # Check pre-requirements for space manipulation
115
123
  if target_data["space"] == "native":
116
124
  # Warp data check
@@ -138,27 +146,37 @@ def compute_brain_mask(
138
146
  )
139
147
  template = extra_input[key]["data"]
140
148
  template_space = extra_input[key]["space"]
149
+ logger.debug(f"Using {key} in {template_space} for mask computation.")
141
150
  else:
151
+ template_resolution = None
152
+ if template_space is None:
153
+ template_space = target_std_space
154
+ elif template_space != target_std_space:
155
+ # We re going to warp, so get the highest resolution
156
+ template_resolution = "highest"
157
+
142
158
  # Fetch template in closest resolution
143
159
  template = get_template(
144
- space=target_std_space,
160
+ space=template_space,
145
161
  target_img=target_data["data"],
146
162
  extra_input=None,
147
163
  template_type=mask_type,
164
+ resolution=template_resolution,
148
165
  )
149
- template_space = target_std_space
166
+
167
+ mask_name = f"template_{target_std_space}_for_compute_brain_mask"
150
168
  # Resample and warp template if target space is native
151
169
  if target_data["space"] == "native" and template_space != "native":
152
170
  if warp_data["warper"] == "fsl":
153
171
  resampled_template = FSLMaskWarper().warp(
154
- mask_name=f"template_{target_std_space}_for_compute_brain_mask",
172
+ mask_name=mask_name,
155
173
  mask_img=template,
156
174
  target_data=target_data,
157
175
  warp_data=warp_data,
158
176
  )
159
177
  elif warp_data["warper"] == "ants":
160
178
  resampled_template = ANTsMaskWarper().warp(
161
- mask_name=f"template_{target_std_space}_for_compute_brain_mask",
179
+ mask_name=mask_name,
162
180
  # use template here
163
181
  mask_img=template,
164
182
  src=target_std_space,
@@ -166,15 +184,31 @@ def compute_brain_mask(
166
184
  target_data=target_data,
167
185
  warp_data=warp_data,
168
186
  )
169
- # Resample template to target image
170
187
  else:
188
+ # Warp template to correct space
189
+ if template_space != target_std_space:
190
+ logger.debug(
191
+ f"Warping template to {target_std_space} space using ANTs."
192
+ )
193
+ template = ANTsMaskWarper().warp(
194
+ mask_name=mask_name,
195
+ mask_img=template,
196
+ src=template_space,
197
+ dst=target_std_space,
198
+ target_data=target_data,
199
+ warp_data=None,
200
+ )
201
+ # Resample template to target image
171
202
  resampled_template = nimg.resample_to_img(
172
- source_img=template, target_img=target_data["data"]
203
+ source_img=template,
204
+ target_img=target_data["data"],
205
+ interpolation=_get_interpolation_method(template),
173
206
  )
174
207
 
175
208
  # Threshold resampled template and get mask
209
+ logger.debug("Thresholding template to get mask.")
176
210
  mask = (nimg.get_data(resampled_template) >= threshold).astype("int8")
177
-
211
+ logger.debug("Mask computation from brain template complete.")
178
212
  return nimg.new_img_like(target_data["data"], mask) # type: ignore
179
213
 
180
214
 
@@ -526,6 +560,7 @@ class MaskRegistry(BasePipelineDataRegistry, metaclass=Singleton):
526
560
  mask_img = nimg.resample_to_img(
527
561
  source_img=mask_img,
528
562
  target_img=target_data["data"],
563
+ interpolation=_get_interpolation_method(mask_img),
529
564
  )
530
565
  # Starting with new mask
531
566
  else:
@@ -597,6 +632,7 @@ class MaskRegistry(BasePipelineDataRegistry, metaclass=Singleton):
597
632
  mask_img = nimg.resample_to_img(
598
633
  source_img=mask_img,
599
634
  target_img=target_img,
635
+ interpolation=_get_interpolation_method(mask_img),
600
636
  )
601
637
  else:
602
638
  # Warp mask if target space is native as
@@ -726,3 +762,23 @@ def _load_ukb_mask(name: str) -> Path:
726
762
  mask_fname = _masks_path / "ukb" / mask_fname
727
763
 
728
764
  return mask_fname
765
+
766
+
767
+ def _get_interpolation_method(img: "Nifti1Image") -> str:
768
+ """Get correct interpolation method for `img`.
769
+
770
+ Parameters
771
+ ----------
772
+ img : nibabel.nifti1.Nifti1Image
773
+ The image.
774
+
775
+ Returns
776
+ -------
777
+ str
778
+ The interpolation method.
779
+
780
+ """
781
+ if np.array_equal(np.unique(img.get_fdata()), [0, 1]):
782
+ return "nearest"
783
+ else:
784
+ return "continuous"
@@ -125,6 +125,7 @@ def get_template(
125
125
  target_img: nib.Nifti1Image,
126
126
  extra_input: Optional[dict[str, Any]] = None,
127
127
  template_type: str = "T1w",
128
+ resolution: Optional[Union[int, "str"]] = None,
128
129
  ) -> nib.Nifti1Image:
129
130
  """Get template for the space, tailored for the target image.
130
131
 
@@ -140,6 +141,10 @@ def get_template(
140
141
  types (default None).
141
142
  template_type : {"T1w", "brain", "gm", "wm", "csf"}, optional
142
143
  The template type to retrieve (default "T1w").
144
+ resolution : int or "highest", optional
145
+ The resolution of the template to fetch. If None, the closest
146
+ resolution to the target image is used (default None). If "highest",
147
+ the highest resolution is used.
143
148
 
144
149
  Returns
145
150
  -------
@@ -149,7 +154,8 @@ def get_template(
149
154
  Raises
150
155
  ------
151
156
  ValueError
152
- If ``space`` or ``template_type`` is invalid.
157
+ If ``space`` or ``template_type`` is invalid or
158
+ if ``resolution`` is not at int or "highest".
153
159
  RuntimeError
154
160
  If required template is not found.
155
161
 
@@ -162,18 +168,30 @@ def get_template(
162
168
  if template_type not in ["T1w", "brain", "gm", "wm", "csf"]:
163
169
  raise_error(f"Unknown template type: {template_type}")
164
170
 
165
- # Get the min of the voxels sizes and use it as the resolution
166
- resolution = np.min(target_img.header.get_zooms()[:3]).astype(int)
171
+ if isinstance(resolution, str) and resolution != "highest":
172
+ raise_error(
173
+ "Invalid resolution value. Must be an integer or 'highest'"
174
+ )
167
175
 
168
176
  # Fetch available resolutions for the template
169
177
  available_resolutions = [
170
178
  int(min(val["zooms"]))
171
179
  for val in tflow.get_metadata(space)["res"].values()
172
180
  ]
181
+
182
+ # Get the min of the voxels sizes and use it as the resolution
183
+ if resolution is None:
184
+ resolution = np.min(target_img.header.get_zooms()[:3]).astype(int)
185
+ elif resolution == "highest":
186
+ resolution = 0
187
+
173
188
  # Use the closest resolution if desired resolution is not found
174
189
  resolution = closest_resolution(resolution, available_resolutions)
175
190
 
176
- logger.info(f"Downloading template {space} in resolution {resolution}")
191
+ logger.info(
192
+ f"Downloading template {space} ({template_type} in "
193
+ f"resolution {resolution}"
194
+ )
177
195
  # Retrieve template
178
196
  try:
179
197
  suffix = None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: junifer
3
- Version: 0.0.6.dev366
3
+ Version: 0.0.6.dev378
4
4
  Summary: JUelich NeuroImaging FEature extractoR
5
5
  Author-email: Fede Raimondo <f.raimondo@fz-juelich.de>, Synchon Mandal <s.mandal@fz-juelich.de>
6
6
  Maintainer-email: Fede Raimondo <f.raimondo@fz-juelich.de>, Synchon Mandal <s.mandal@fz-juelich.de>
@@ -1,6 +1,6 @@
1
1
  junifer/__init__.py,sha256=2McgH1yNue6Z1V26-uN_mfMjbTcx4CLhym-DMBl5xA4,266
2
2
  junifer/__init__.pyi,sha256=SsTvgq2Dod6UqJN96GH1lCphH6hJQQurEJHGNhHjGUI,508
3
- junifer/_version.py,sha256=t-3cRoRiLKJo00UFpf7tn_vToC9PLS2rVeH50rEhEGs,428
3
+ junifer/_version.py,sha256=oYfebrtAvhll95HaoOLZCO0IYYGaqQpsY-CgDqPH8w4,428
4
4
  junifer/conftest.py,sha256=PWYkkRDU8ly2lYwv7VBKMHje4et6HX7Yey3Md_I2KbA,613
5
5
  junifer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  junifer/stats.py,sha256=e9aaagMGtgpRfW3Wdpz9ocpnYld1IWylCDcjFUgX9Mk,6225
@@ -75,7 +75,7 @@ junifer/data/__init__.pyi,sha256=qYszjUYcbFi_2zO23MnbA2HhTW-Ad2oh1pqPQYd6yt0,542
75
75
  junifer/data/_dispatch.py,sha256=O524U1R4MtbGhGJsL0HSh9EqisapBFJWK7uupXrJuMg,6158
76
76
  junifer/data/pipeline_data_registry_base.py,sha256=G8bE3WTj4D_rKC4ZKZe6E48Sd96CGea1PS3SxmTgGK4,2010
77
77
  junifer/data/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
78
- junifer/data/template_spaces.py,sha256=JPsrGyfbaVgZ75uB3ci2CUuVxUg3NDi6HhO_VTIHeOE,6508
78
+ junifer/data/template_spaces.py,sha256=wkxaDA_I_W5FB42GPxOKrl_A_fp0Crdrh6wFux27_NA,7147
79
79
  junifer/data/utils.py,sha256=5r-0QGQCNZvDM1tVcl9xyrIdgAO85mww0plpM1RUaGA,3247
80
80
  junifer/data/coordinates/__init__.py,sha256=ffM8rwcHLgHAWixJbKrATrbUKzX940V1UF6RAxZdUMg,186
81
81
  junifer/data/coordinates/__init__.pyi,sha256=Z-Ti5XD3HigkZ8uYN6oYsLqw40-F1GvTVQ5QAy08Wng,88
@@ -104,9 +104,9 @@ junifer/data/coordinates/VOIs/meta/extDMN_VOIs.txt,sha256=Ogx1QvqZcnXDM3ncF2ha78
104
104
  junifer/data/coordinates/tests/test_coordinates.py,sha256=_c2P4oaDGpsmui5gJBe_jN6HLGiKxONkYPR69sRBUlU,4219
105
105
  junifer/data/masks/__init__.py,sha256=eEEhHglyVEx1LrqwXjq3cOmjf4sTsgBstRx5-k7zIQU,180
106
106
  junifer/data/masks/__init__.pyi,sha256=lcgr8gmWDPibC4RxnWBXb8DDpIkO73Aax09u6VXiJJI,114
107
- junifer/data/masks/_ants_mask_warper.py,sha256=Mwgc2_ZMf28vS_-fviRvZnHyT7JoQ1cQLozo7nUZSyM,5350
108
- junifer/data/masks/_fsl_mask_warper.py,sha256=VApp-ofGBKePNmCdgTg1HoEA66lMQiAPT0ihkhB2ezY,2415
109
- junifer/data/masks/_masks.py,sha256=ZJ5kmZQCL854k0gwXQhsWnWVI2hzdDnz2O6kIe2Ih2A,26194
107
+ junifer/data/masks/_ants_mask_warper.py,sha256=JLK2Jh2AOAiv_NoUGhRoTBEhRFXPRXTDPmQGH9vBSok,5805
108
+ junifer/data/masks/_fsl_mask_warper.py,sha256=YZOMlRgQ7_4shnXNc_05tmwDk5xHI-1wqle-RdNsJ34,2857
109
+ junifer/data/masks/_masks.py,sha256=ZR0pfwIBiyJ3tGKo0mHW5Pu5asaZHeTvYbgJrUwZh24,28172
110
110
  junifer/data/masks/tests/test_masks.py,sha256=W0bzRB5Bp-iGO44VtEmaf7BuT-joe_2tQI0lma5NQHA,16090
111
111
  junifer/data/masks/ukb/UKB_15K_GM_template.nii.gz,sha256=jcX1pDOrDsoph8cPMNFVKH5gZYio5G4rJNpOFXm9wJI,946636
112
112
  junifer/data/masks/vickery-patil/CAT12_IXI555_MNI152_TMP_GS_GMprob0.2_clean.nii.gz,sha256=j6EY8EtRnUuRxeKgD65Q6B0GPEPIALKDJEIje1TfnAU,88270
@@ -341,10 +341,10 @@ junifer/utils/tests/test_config.py,sha256=7ltIXuwb_W4Mv_1dxQWyiyM10XgUAfsWKV6D_i
341
341
  junifer/utils/tests/test_fs.py,sha256=WQS7cKlKEZ742CIuiOYYpueeAhY9PqlastfDVpVVtvE,923
342
342
  junifer/utils/tests/test_helpers.py,sha256=k5qqfxK8dFyuewTJyR1Qn6-nFaYNuVr0ysc18bfPjyU,929
343
343
  junifer/utils/tests/test_logging.py,sha256=duO4ou365hxwa_kwihFtKPLaL6LC5XHiyhOijrrngbA,8009
344
- junifer-0.0.6.dev366.dist-info/AUTHORS.rst,sha256=rmULKpchpSol4ExWFdm-qu4fkpSZPYqIESVJBZtGb6E,163
345
- junifer-0.0.6.dev366.dist-info/LICENSE.md,sha256=MqCnOBu8uXsEOzRZWh9EBVfVz-kE9NkXcLCrtGXo2yU,34354
346
- junifer-0.0.6.dev366.dist-info/METADATA,sha256=1bnN5jpKnbFsIC94NMUL1xwIOpl-LhdAYQmDFrKNa3g,8429
347
- junifer-0.0.6.dev366.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
348
- junifer-0.0.6.dev366.dist-info/entry_points.txt,sha256=6O8ru0BP-SP7YMUZiizFNoaZ2HvJpadO2G7nKk4PwjI,48
349
- junifer-0.0.6.dev366.dist-info/top_level.txt,sha256=4bAq1R2QFQ4b3hohjys2JBvxrl0GKk5LNFzYvz9VGcA,8
350
- junifer-0.0.6.dev366.dist-info/RECORD,,
344
+ junifer-0.0.6.dev378.dist-info/AUTHORS.rst,sha256=rmULKpchpSol4ExWFdm-qu4fkpSZPYqIESVJBZtGb6E,163
345
+ junifer-0.0.6.dev378.dist-info/LICENSE.md,sha256=MqCnOBu8uXsEOzRZWh9EBVfVz-kE9NkXcLCrtGXo2yU,34354
346
+ junifer-0.0.6.dev378.dist-info/METADATA,sha256=1i1ttfHKfKnLZhJQxPgbjBGDS5rw3l7O1R0OtIldFO4,8429
347
+ junifer-0.0.6.dev378.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
348
+ junifer-0.0.6.dev378.dist-info/entry_points.txt,sha256=6O8ru0BP-SP7YMUZiizFNoaZ2HvJpadO2G7nKk4PwjI,48
349
+ junifer-0.0.6.dev378.dist-info/top_level.txt,sha256=4bAq1R2QFQ4b3hohjys2JBvxrl0GKk5LNFzYvz9VGcA,8
350
+ junifer-0.0.6.dev378.dist-info/RECORD,,