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 +2 -2
- junifer/data/masks/_ants_mask_warper.py +22 -1
- junifer/data/masks/_fsl_mask_warper.py +22 -1
- junifer/data/masks/_masks.py +63 -7
- junifer/data/template_spaces.py +22 -4
- {junifer-0.0.6.dev366.dist-info → junifer-0.0.6.dev378.dist-info}/METADATA +1 -1
- {junifer-0.0.6.dev366.dist-info → junifer-0.0.6.dev378.dist-info}/RECORD +12 -12
- {junifer-0.0.6.dev366.dist-info → junifer-0.0.6.dev378.dist-info}/AUTHORS.rst +0 -0
- {junifer-0.0.6.dev366.dist-info → junifer-0.0.6.dev378.dist-info}/LICENSE.md +0 -0
- {junifer-0.0.6.dev366.dist-info → junifer-0.0.6.dev378.dist-info}/WHEEL +0 -0
- {junifer-0.0.6.dev366.dist-info → junifer-0.0.6.dev378.dist-info}/entry_points.txt +0 -0
- {junifer-0.0.6.dev366.dist-info → junifer-0.0.6.dev378.dist-info}/top_level.txt +0 -0
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.
|
16
|
-
__version_tuple__ = version_tuple = (0, 0, 6, '
|
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
|
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=
|
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()}",
|
junifer/data/masks/_masks.py
CHANGED
@@ -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=
|
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
|
-
|
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=
|
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=
|
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,
|
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"
|
junifer/data/template_spaces.py
CHANGED
@@ -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
|
-
|
166
|
-
|
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(
|
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.
|
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=
|
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=
|
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=
|
108
|
-
junifer/data/masks/_fsl_mask_warper.py,sha256=
|
109
|
-
junifer/data/masks/_masks.py,sha256=
|
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.
|
345
|
-
junifer-0.0.6.
|
346
|
-
junifer-0.0.6.
|
347
|
-
junifer-0.0.6.
|
348
|
-
junifer-0.0.6.
|
349
|
-
junifer-0.0.6.
|
350
|
-
junifer-0.0.6.
|
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,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|