junifer 0.0.2.dev138__py3-none-any.whl → 0.0.2.dev140__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.
Files changed (38) hide show
  1. junifer/_version.py +2 -2
  2. junifer/data/masks.py +102 -30
  3. junifer/data/tests/test_masks.py +166 -8
  4. junifer/datagrabber/aomic/id1000.py +33 -1
  5. junifer/datagrabber/aomic/piop1.py +14 -0
  6. junifer/datagrabber/aomic/piop2.py +35 -1
  7. junifer/datagrabber/aomic/tests/test_id1000.py +6 -0
  8. junifer/datagrabber/aomic/tests/test_piop1.py +6 -0
  9. junifer/datagrabber/aomic/tests/test_piop2.py +6 -1
  10. junifer/markers/ets_rss.py +8 -8
  11. junifer/markers/falff/falff_parcels.py +7 -7
  12. junifer/markers/falff/falff_spheres.py +8 -8
  13. junifer/markers/functional_connectivity/crossparcellation_functional_connectivity.py +8 -8
  14. junifer/markers/functional_connectivity/edge_functional_connectivity_parcels.py +7 -7
  15. junifer/markers/functional_connectivity/edge_functional_connectivity_spheres.py +8 -8
  16. junifer/markers/functional_connectivity/functional_connectivity_base.py +7 -7
  17. junifer/markers/functional_connectivity/functional_connectivity_parcels.py +7 -7
  18. junifer/markers/functional_connectivity/functional_connectivity_spheres.py +8 -8
  19. junifer/markers/functional_connectivity/tests/test_functional_connectivity_base.py +4 -2
  20. junifer/markers/parcel_aggregation.py +11 -9
  21. junifer/markers/reho/reho_parcels.py +8 -8
  22. junifer/markers/reho/reho_spheres.py +8 -8
  23. junifer/markers/sphere_aggregation.py +11 -9
  24. junifer/markers/tests/test_collection.py +8 -6
  25. junifer/markers/tests/test_marker_utils.py +2 -1
  26. junifer/markers/tests/test_parcel_aggregation.py +2 -2
  27. junifer/markers/tests/test_sphere_aggregation.py +1 -1
  28. junifer/markers/utils.py +10 -10
  29. junifer/preprocess/base.py +11 -1
  30. junifer/preprocess/confounds/fmriprep_confound_remover.py +31 -24
  31. junifer/preprocess/confounds/tests/test_fmriprep_confound_remover.py +59 -3
  32. {junifer-0.0.2.dev138.dist-info → junifer-0.0.2.dev140.dist-info}/METADATA +1 -1
  33. {junifer-0.0.2.dev138.dist-info → junifer-0.0.2.dev140.dist-info}/RECORD +38 -38
  34. {junifer-0.0.2.dev138.dist-info → junifer-0.0.2.dev140.dist-info}/AUTHORS.rst +0 -0
  35. {junifer-0.0.2.dev138.dist-info → junifer-0.0.2.dev140.dist-info}/LICENSE.md +0 -0
  36. {junifer-0.0.2.dev138.dist-info → junifer-0.0.2.dev140.dist-info}/WHEEL +0 -0
  37. {junifer-0.0.2.dev138.dist-info → junifer-0.0.2.dev140.dist-info}/entry_points.txt +0 -0
  38. {junifer-0.0.2.dev138.dist-info → junifer-0.0.2.dev140.dist-info}/top_level.txt +0 -0
junifer/_version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # file generated by setuptools_scm
2
2
  # don't change, don't track in version control
3
- __version__ = version = '0.0.2.dev138'
4
- __version_tuple__ = version_tuple = (0, 0, 2, 'dev138')
3
+ __version__ = version = '0.0.2.dev140'
4
+ __version_tuple__ = version_tuple = (0, 0, 2, 'dev140')
junifer/data/masks.py CHANGED
@@ -23,6 +23,7 @@ from nilearn.masking import (
23
23
  compute_background_mask,
24
24
  compute_brain_mask,
25
25
  compute_epi_mask,
26
+ intersect_masks,
26
27
  )
27
28
 
28
29
  from ..utils.logging import logger, raise_error
@@ -36,7 +37,10 @@ if TYPE_CHECKING:
36
37
  _masks_path = Path(__file__).parent / "masks"
37
38
 
38
39
 
39
- def _fetch_icbm152_brain_gm_mask(target_img: "Nifti1Image", **kwargs):
40
+ def _fetch_icbm152_brain_gm_mask(
41
+ target_img: "Nifti1Image",
42
+ **kwargs,
43
+ ):
40
44
  """Fetch ICBM152 brain mask and resample.
41
45
 
42
46
  Parameters
@@ -65,6 +69,9 @@ data.
65
69
 
66
70
  The built-in masks are files that are shipped with the package in the
67
71
  data/masks directory. The user can also register their own masks.
72
+
73
+ Callable masks should be functions that take at least one parameter:
74
+ * `target_img`: the image to which the mask will be applied.
68
75
  """
69
76
  _available_masks: Dict[str, Dict[str, Any]] = {
70
77
  "GM_prob0.2": {"family": "Vickery-Patil"},
@@ -146,19 +153,23 @@ def list_masks() -> List[str]:
146
153
 
147
154
 
148
155
  def get_mask(
149
- mask: Union[str, Dict],
156
+ masks: Union[str, Dict, List[Union[Dict, str]]],
150
157
  target_data: Dict[str, Any],
158
+ extra_input: Optional[Dict[str, Any]] = None,
151
159
  ) -> "Nifti1Image":
152
160
  """Get mask, tailored for the target image.
153
161
 
154
162
  Parameters
155
163
  ----------
156
- masks : str or dict
164
+ masks : str, dict or list of dict or str
157
165
  The name of the mask, or the name of a callable mask and the parameters
158
- of the mask.
166
+ of the mask as a dictionary. Several masks can be passed as a list.
159
167
  target_data : dict
160
168
  The corresponding item of the data object to which the mask will be
161
169
  applied.
170
+ extra_input : dict, optional
171
+ The other fields in the data object. Useful for accessing other data
172
+ kinds that needs to be used in the computation of masks (default None).
162
173
 
163
174
  Returns
164
175
  -------
@@ -167,37 +178,98 @@ def get_mask(
167
178
  """
168
179
  # Get the min of the voxels sizes and use it as the resolution
169
180
  target_img = target_data["data"]
181
+ inherited_mask_item = target_data.get("mask_item", None)
170
182
  resolution = np.min(target_img.header.get_zooms()[:3])
171
183
 
172
- if isinstance(mask, dict):
173
- if len(mask) != 1:
174
- raise_error(
175
- "The mask dictionary must have only one key, "
176
- "the name of the mask."
177
- )
178
- mask_name = list(mask.keys())[0]
179
- mask_params = mask[mask_name]
180
- else:
181
- mask_name = mask
182
- mask_params = None
184
+ if not isinstance(masks, list):
185
+ masks = [masks]
183
186
 
184
- mask_object, _ = load_mask(
185
- mask_name, path_only=False, resolution=resolution
186
- )
187
- if callable(mask_object):
188
- if mask_params is None:
189
- mask_params = {}
190
- mask_img = mask_object(target_img, **mask_params)
191
- else: # Mask is a Nifti1Image
192
- if mask_params is not None:
193
- raise_error("Cannot pass callable params to a non-callable mask.")
194
- mask_img = resample_to_img(
195
- mask_object,
196
- target_img,
197
- interpolation="nearest",
198
- copy=True,
187
+ # Check that dicts have only one key
188
+ invalid_elements = [
189
+ x for x in masks if isinstance(x, dict) and len(x) != 1
190
+ ]
191
+ if len(invalid_elements) > 0:
192
+ raise_error(
193
+ "Each of the masks dictionary must have only one key, "
194
+ "the name of the mask. The following dictionaries are invalid: "
195
+ f"{invalid_elements}"
199
196
  )
200
197
 
198
+ # Check params for the intersection function
199
+ intersect_params = {}
200
+ true_masks = []
201
+ for t_mask in masks:
202
+ if isinstance(t_mask, dict):
203
+ if "threshold" in t_mask:
204
+ intersect_params["threshold"] = t_mask["threshold"]
205
+ continue
206
+ elif "connected" in t_mask:
207
+ intersect_params["connected"] = t_mask["connected"]
208
+ continue
209
+ # All the other elements are masks
210
+ true_masks.append(t_mask)
211
+
212
+ if len(true_masks) == 0:
213
+ raise_error("No mask was passed. At least one mask is required.")
214
+ # Get all the masks
215
+ all_masks = []
216
+ for t_mask in true_masks:
217
+ if isinstance(t_mask, dict):
218
+ mask_name = list(t_mask.keys())[0]
219
+ mask_params = t_mask[mask_name]
220
+ else:
221
+ mask_name = t_mask
222
+ mask_params = None
223
+
224
+ if mask_name == "inherit":
225
+ if extra_input is None:
226
+ raise_error(
227
+ "Cannot inherit mask from another data item "
228
+ "because no extra data was passed."
229
+ )
230
+ if inherited_mask_item is None:
231
+ raise_error(
232
+ "Cannot inherit mask from another data item "
233
+ "because no mask item was specified "
234
+ "(missing `mask_item` key in the data object)."
235
+ )
236
+ if inherited_mask_item not in extra_input:
237
+ raise_error(
238
+ "Cannot inherit mask from another data item "
239
+ f"because the item ({inherited_mask_item}) does not exist."
240
+ )
241
+ mask_img = extra_input[inherited_mask_item]["data"]
242
+ else:
243
+ mask_object, _ = load_mask(
244
+ mask_name, path_only=False, resolution=resolution
245
+ )
246
+ if callable(mask_object):
247
+ if mask_params is None:
248
+ mask_params = {}
249
+ mask_img = mask_object(target_img, **mask_params)
250
+ else: # Mask is a Nifti1Image
251
+ if mask_params is not None:
252
+ raise_error(
253
+ "Cannot pass callable params to a non-callable mask."
254
+ )
255
+ mask_img = resample_to_img(
256
+ mask_object,
257
+ target_img,
258
+ interpolation="nearest",
259
+ copy=True,
260
+ )
261
+ all_masks.append(mask_img)
262
+ if len(all_masks) > 1:
263
+ mask_img = intersect_masks(all_masks, **intersect_params)
264
+ else:
265
+ if len(intersect_params) > 0:
266
+ # Yes, I'm this strict!
267
+ raise_error(
268
+ "Cannot pass parameters to the intersection function "
269
+ "when there is only one mask."
270
+ )
271
+ mask_img = all_masks[0]
272
+
201
273
  return mask_img
202
274
 
203
275
 
@@ -6,8 +6,9 @@
6
6
  # License: AGPL
7
7
 
8
8
  from pathlib import Path
9
- from typing import Callable, Dict, Union
9
+ from typing import Callable, Dict, List, Union
10
10
 
11
+ import numpy as np
11
12
  import pytest
12
13
  from nilearn.datasets import fetch_icbm152_brain_gm_mask
13
14
  from nilearn.image import resample_to_img
@@ -15,6 +16,7 @@ from nilearn.masking import (
15
16
  compute_background_mask,
16
17
  compute_brain_mask,
17
18
  compute_epi_mask,
19
+ intersect_masks,
18
20
  )
19
21
  from numpy.testing import assert_array_almost_equal, assert_array_equal
20
22
 
@@ -56,7 +58,9 @@ def test_register_mask_already_registered() -> None:
56
58
  name="testmask",
57
59
  mask_path="testmask.nii.gz",
58
60
  )
59
- assert load_mask("testmask", path_only=True)[1].name == "testmask.nii.gz"
61
+ out = load_mask("testmask", path_only=True)
62
+ assert out[1] is not None
63
+ assert out[1].name == "testmask.nii.gz"
60
64
 
61
65
  # Try registering again
62
66
  with pytest.raises(ValueError, match=r"already registered."):
@@ -70,7 +74,9 @@ def test_register_mask_already_registered() -> None:
70
74
  overwrite=True,
71
75
  )
72
76
 
73
- assert load_mask("testmask", path_only=True)[1].name == "testmask2.nii.gz"
77
+ out = load_mask("testmask", path_only=True)
78
+ assert out[1] is not None
79
+ assert out[1].name == "testmask2.nii.gz"
74
80
 
75
81
 
76
82
  @pytest.mark.parametrize(
@@ -110,6 +116,7 @@ def test_register_mask(
110
116
  # Load registered mask
111
117
  _, fname = load_mask(name=name, path_only=True)
112
118
  # Check values for registered mask
119
+ assert fname is not None
113
120
  assert fname.name == f"{name}.nii.gz"
114
121
 
115
122
 
@@ -146,6 +153,7 @@ def test_vickery_patil() -> None:
146
153
  mask.header["pixdim"][1:4], [1.5, 1.5, 1.5] # type: ignore
147
154
  )
148
155
 
156
+ assert fname is not None
149
157
  assert fname.name == "CAT12_IXI555_MNI152_TMP_GS_GMprob0.2_clean.nii.gz"
150
158
 
151
159
  mask, fname = load_mask("GM_prob0.2", resolution=3)
@@ -153,6 +161,7 @@ def test_vickery_patil() -> None:
153
161
  mask.header["pixdim"][1:4], [3.0, 3.0, 3.0] # type: ignore
154
162
  )
155
163
 
164
+ assert fname is not None
156
165
  assert (
157
166
  fname.name == "CAT12_IXI555_MNI152_TMP_GS_GMprob0.2_clean_3mm.nii.gz"
158
167
  )
@@ -162,6 +171,7 @@ def test_vickery_patil() -> None:
162
171
  mask.header["pixdim"][1:4], [3.0, 3.0, 3.0] # type: ignore
163
172
  )
164
173
 
174
+ assert fname is not None
165
175
  assert fname.name == "GMprob0.2_cortex_3mm_NA_rm.nii.gz"
166
176
 
167
177
  with pytest.raises(ValueError, match=r"find a Vickery-Patil mask "):
@@ -176,7 +186,7 @@ def test_get_mask() -> None:
176
186
  input = reader.fit_transform(input)
177
187
  vbm_gm = input["VBM_GM"]
178
188
  vbm_gm_img = vbm_gm["data"]
179
- mask = get_mask(mask="GM_prob0.2", target_data=vbm_gm)
189
+ mask = get_mask(masks="GM_prob0.2", target_data=vbm_gm)
180
190
 
181
191
  assert mask.shape == vbm_gm_img.shape
182
192
  assert_array_equal(mask.affine, vbm_gm_img.affine)
@@ -204,7 +214,7 @@ def test_mask_callable() -> None:
204
214
  input = reader.fit_transform(input)
205
215
  vbm_gm = input["VBM_GM"]
206
216
  vbm_gm_img = vbm_gm["data"]
207
- mask = get_mask(mask="identity", target_data=vbm_gm)
217
+ mask = get_mask(masks="identity", target_data=vbm_gm)
208
218
 
209
219
  assert_array_equal(mask.get_fdata(), vbm_gm_img.get_fdata())
210
220
 
@@ -218,11 +228,48 @@ def test_get_mask_errors() -> None:
218
228
  input = dg["sub-01"]
219
229
  input = reader.fit_transform(input)
220
230
  vbm_gm = input["VBM_GM"]
231
+ # Test wrong masks definitions (more than one key per dict)
221
232
  with pytest.raises(ValueError, match=r"only one key"):
222
- get_mask(mask={"GM_prob0.2": {}, "Other": {}}, target_data=vbm_gm)
233
+ get_mask(masks={"GM_prob0.2": {}, "Other": {}}, target_data=vbm_gm)
223
234
 
235
+ # Test wrong masks definitions (pass paramaeters to non-callable mask)
224
236
  with pytest.raises(ValueError, match=r"callable params"):
225
- get_mask(mask={"GM_prob0.2": {"param": 1}}, target_data=vbm_gm)
237
+ get_mask(masks={"GM_prob0.2": {"param": 1}}, target_data=vbm_gm)
238
+
239
+ # Pass only parametesr to the intersection function
240
+ with pytest.raises(
241
+ ValueError, match=r" At least one mask is required."
242
+ ):
243
+ get_mask(masks={"threshold": 1}, target_data=vbm_gm)
244
+
245
+ # Pass parameters to the intersection function when only one mask
246
+ with pytest.raises(
247
+ ValueError, match=r"parameters to the intersection"
248
+ ):
249
+ get_mask(
250
+ masks=["GM_prob0.2", {"threshold": 1}], target_data=vbm_gm
251
+ )
252
+
253
+ # Test "inherited" masks errors
254
+
255
+ # 1) No extra_data parameter
256
+ with pytest.raises(ValueError, match=r"no extra data was passed"):
257
+ get_mask(masks="inherit", target_data=vbm_gm)
258
+
259
+ extra_input = {"VBM_MASK": {}}
260
+
261
+ # 2) No mask_item key in target_data
262
+ with pytest.raises(ValueError, match=r"no mask item was specified"):
263
+ get_mask(
264
+ masks="inherit", target_data=vbm_gm, extra_input=extra_input
265
+ )
266
+
267
+ # 3) mask_item not in extra data
268
+ with pytest.raises(ValueError, match=r"does not exist"):
269
+ vbm_gm["mask_item"] = "wrong"
270
+ get_mask(
271
+ masks="inherit", target_data=vbm_gm, extra_input=extra_input
272
+ )
226
273
 
227
274
 
228
275
  @pytest.mark.parametrize(
@@ -271,7 +318,7 @@ def test_nilearn_compute_masks(
271
318
  else:
272
319
  mask_spec = {mask_name: params}
273
320
 
274
- mask = get_mask(mask=mask_spec, target_data=bold)
321
+ mask = get_mask(masks=mask_spec, target_data=bold)
275
322
 
276
323
  assert_array_equal(mask.affine, bold_img.affine)
277
324
 
@@ -287,3 +334,114 @@ def test_nilearn_compute_masks(
287
334
  copy=True,
288
335
  )
289
336
  assert_array_equal(mask.get_fdata(), ni_mask.get_fdata())
337
+
338
+
339
+ def test_get_mask_inherit() -> None:
340
+ """Test using the inherit mask functionality."""
341
+ reader = DefaultDataReader()
342
+ with SPMAuditoryTestingDatagrabber() as dg:
343
+ input = dg["sub001"]
344
+ input = reader.fit_transform(input)
345
+ # Compute brain mask using nilearn
346
+ gm_mask = compute_brain_mask(input["BOLD"]["data"], threshold=0.2)
347
+
348
+ # Get mask using the compute_brain_mask function
349
+ mask1 = get_mask(
350
+ masks={"compute_brain_mask": {"threshold": 0.2}},
351
+ target_data=input["BOLD"],
352
+ )
353
+
354
+ # Now get the mask using the inherit functionality, passing the
355
+ # computed mask as extra data
356
+ extra_input = {"BOLD_MASK": {"data": gm_mask}}
357
+ input["BOLD"]["mask_item"] = "BOLD_MASK"
358
+ mask2 = get_mask(
359
+ masks="inherit", target_data=input["BOLD"], extra_input=extra_input
360
+ )
361
+
362
+ # Both masks should be equal
363
+ assert_array_equal(mask1.get_fdata(), mask2.get_fdata())
364
+
365
+
366
+ @pytest.mark.parametrize(
367
+ "masks,params",
368
+ [
369
+ (["GM_prob0.2", "compute_brain_mask"], {}),
370
+ (
371
+ ["GM_prob0.2", "compute_brain_mask"],
372
+ {"threshold": 0.2},
373
+ ),
374
+ (
375
+ [
376
+ "GM_prob0.2",
377
+ "compute_brain_mask",
378
+ "fetch_icbm152_brain_gm_mask",
379
+ ],
380
+ {"threshold": 1, "connected": True},
381
+ ),
382
+ ],
383
+ )
384
+ def test_get_mask_multiple(
385
+ masks: Union[str, Dict, List[Union[Dict, str]]], params: Dict
386
+ ) -> None:
387
+ """Test getting multiple masks.
388
+
389
+ Parameters
390
+ ----------
391
+ masks : str, dict, list of str or dict
392
+ Masks to get, junifer style.
393
+ params : dict
394
+ Parameters to pass to the intersect_masks function.
395
+ """
396
+ reader = DefaultDataReader()
397
+ with SPMAuditoryTestingDatagrabber() as dg:
398
+ input = dg["sub001"]
399
+ input = reader.fit_transform(input)
400
+ if not isinstance(masks, list):
401
+ junifer_masks = [masks]
402
+ else:
403
+ junifer_masks = masks.copy()
404
+ if len(params) > 0:
405
+ # Convert params to junifer style (one dict per param)
406
+ junifer_params = [{k: params[k]} for k in params.keys()]
407
+ junifer_masks.extend(junifer_params)
408
+ target_img = input["BOLD"]["data"]
409
+ resolution = np.min(target_img.header.get_zooms()[:3])
410
+
411
+ computed = get_mask(masks=junifer_masks, target_data=input["BOLD"])
412
+
413
+ masks_names = [
414
+ list(x.keys())[0] if isinstance(x, dict) else x for x in masks
415
+ ]
416
+
417
+ mask_funcs = [
418
+ x
419
+ for x in masks_names
420
+ if _available_masks[x]["family"] == "Callable"
421
+ ]
422
+ mask_files = [
423
+ x
424
+ for x in masks_names
425
+ if _available_masks[x]["family"] != "Callable"
426
+ ]
427
+
428
+ mask_imgs = [
429
+ load_mask(t_mask, path_only=False, resolution=resolution)[0]
430
+ for t_mask in mask_files
431
+ ]
432
+
433
+ for t_func in mask_funcs:
434
+ mask_imgs.append(_available_masks[t_func]["func"](target_img))
435
+
436
+ mask_imgs = [
437
+ resample_to_img(
438
+ t_mask,
439
+ target_img,
440
+ interpolation="nearest",
441
+ copy=True,
442
+ )
443
+ for t_mask in mask_imgs
444
+ ]
445
+
446
+ expected = intersect_masks(mask_imgs, **params)
447
+ assert_array_equal(computed.get_fdata(), expected.get_fdata())
@@ -7,7 +7,7 @@
7
7
  # License: AGPL
8
8
 
9
9
  from pathlib import Path
10
- from typing import Union
10
+ from typing import Union, Dict
11
11
 
12
12
  from junifer.datagrabber import PatternDataladDataGrabber
13
13
 
@@ -34,7 +34,9 @@ class DataladAOMICID1000(PatternDataladDataGrabber):
34
34
  types = [
35
35
  "BOLD",
36
36
  "BOLD_confounds",
37
+ "BOLD_mask",
37
38
  "T1w",
39
+ "T1w_mask",
38
40
  "probseg_CSF",
39
41
  "probseg_GM",
40
42
  "probseg_WM",
@@ -52,11 +54,22 @@ class DataladAOMICID1000(PatternDataladDataGrabber):
52
54
  "sub-{subject}_task-moviewatching_"
53
55
  "desc-confounds_regressors.tsv"
54
56
  ),
57
+ "BOLD_mask": (
58
+ "derivatives/fmriprep/sub-{subject}/func/"
59
+ "sub-{subject}_task-moviewatching_"
60
+ "space-MNI152NLin2009cAsym_"
61
+ "desc-brain_mask.nii.gz"
62
+ ),
55
63
  "T1w": (
56
64
  "derivatives/fmriprep/sub-{subject}/anat/"
57
65
  "sub-{subject}_space-MNI152NLin2009cAsym_"
58
66
  "desc-preproc_T1w.nii.gz"
59
67
  ),
68
+ "T1w_mask": (
69
+ "derivatives/fmriprep/sub-{subject}/anat/"
70
+ "sub-{subject}_space-MNI152NLin2009cAsym_"
71
+ "desc-brain_mask.nii.gz"
72
+ ),
60
73
  "probseg_CSF": (
61
74
  "derivatives/fmriprep/sub-{subject}/anat/"
62
75
  "sub-{subject}_space-MNI152NLin2009cAsym_label-"
@@ -88,3 +101,22 @@ class DataladAOMICID1000(PatternDataladDataGrabber):
88
101
  replacements=replacements,
89
102
  confounds_format="fmriprep",
90
103
  )
104
+
105
+ def get_item(self, subject: str) -> Dict:
106
+ """Index one element in the dataset.
107
+
108
+ Parameters
109
+ ----------
110
+ subject : str
111
+ The subject ID.
112
+
113
+ Returns
114
+ -------
115
+ out : dict
116
+ Dictionary of paths for each type of data required for the
117
+ specified element.
118
+ """
119
+ out = super().get_item(subject=subject)
120
+ out["BOLD"]["mask_item"] = "BOLD_mask"
121
+ out["T1w"]["mask_item"] = "T1w_mask"
122
+ return out
@@ -41,7 +41,9 @@ class DataladAOMICPIOP1(PatternDataladDataGrabber):
41
41
  types = [
42
42
  "BOLD",
43
43
  "BOLD_confounds",
44
+ "BOLD_mask",
44
45
  "T1w",
46
+ "T1w_mask",
45
47
  "probseg_CSF",
46
48
  "probseg_GM",
47
49
  "probseg_WM",
@@ -83,11 +85,21 @@ class DataladAOMICPIOP1(PatternDataladDataGrabber):
83
85
  "sub-{subject}_task-{task}_"
84
86
  "desc-confounds_regressors.tsv"
85
87
  ),
88
+ "BOLD_mask": (
89
+ "derivatives/fmriprep/sub-{subject}/func/"
90
+ "sub-{subject}_task-{task}_"
91
+ "space-MNI152NLin2009cAsym_desc-brain_mask.nii.gz"
92
+ ),
86
93
  "T1w": (
87
94
  "derivatives/fmriprep/sub-{subject}/anat/"
88
95
  "sub-{subject}_space-MNI152NLin2009cAsym_"
89
96
  "desc-preproc_T1w.nii.gz"
90
97
  ),
98
+ "T1w_mask": (
99
+ "derivatives/fmriprep/sub-{subject}/anat/"
100
+ "sub-{subject}_space-MNI152NLin2009cAsym_"
101
+ "desc-brain_mask.nii.gz"
102
+ ),
91
103
  "probseg_CSF": (
92
104
  "derivatives/fmriprep/sub-{subject}/anat/"
93
105
  "sub-{subject}_space-MNI152NLin2009cAsym_label-"
@@ -149,6 +161,8 @@ class DataladAOMICPIOP1(PatternDataladDataGrabber):
149
161
  new_task = f"{task}_acq-{acq}"
150
162
 
151
163
  out = super().get_item(subject=subject, task=new_task)
164
+ out["BOLD"]["mask_item"] = "BOLD_mask"
165
+ out["T1w"]["mask_item"] = "T1w_mask"
152
166
  return out
153
167
 
154
168
  def get_elements(self) -> List:
@@ -7,7 +7,7 @@
7
7
  # License: AGPL
8
8
 
9
9
  from pathlib import Path
10
- from typing import List, Union
10
+ from typing import List, Union, Dict
11
11
 
12
12
  from junifer.datagrabber import PatternDataladDataGrabber
13
13
 
@@ -40,7 +40,9 @@ class DataladAOMICPIOP2(PatternDataladDataGrabber):
40
40
  types = [
41
41
  "BOLD",
42
42
  "BOLD_confounds",
43
+ "BOLD_mask",
43
44
  "T1w",
45
+ "T1w_mask",
44
46
  "probseg_CSF",
45
47
  "probseg_GM",
46
48
  "probseg_WM",
@@ -80,11 +82,21 @@ class DataladAOMICPIOP2(PatternDataladDataGrabber):
80
82
  "sub-{subject}_task-{task}_acq-seq_"
81
83
  "desc-confounds_regressors.tsv"
82
84
  ),
85
+ "BOLD_mask": (
86
+ "derivatives/fmriprep/sub-{subject}/func/"
87
+ "sub-{subject}_task-{task}_acq-seq_space"
88
+ "-MNI152NLin2009cAsym_desc-brain_mask.nii.gz"
89
+ ),
83
90
  "T1w": (
84
91
  "derivatives/fmriprep/sub-{subject}/anat/"
85
92
  "sub-{subject}_space-MNI152NLin2009cAsym_"
86
93
  "desc-preproc_T1w.nii.gz"
87
94
  ),
95
+ "T1w_mask": (
96
+ "derivatives/fmriprep/sub-{subject}/anat/"
97
+ "sub-{subject}_space-MNI152NLin2009cAsym_"
98
+ "desc-brain_mask.nii.gz"
99
+ ),
88
100
  "probseg_CSF": (
89
101
  "derivatives/fmriprep/sub-{subject}/anat/"
90
102
  "sub-{subject}_space-MNI152NLin2009cAsym_label-"
@@ -127,3 +139,25 @@ class DataladAOMICPIOP2(PatternDataladDataGrabber):
127
139
  """
128
140
  all_elements = super().get_elements()
129
141
  return [x for x in all_elements if x[1] in self.tasks]
142
+
143
+ def get_item(self, subject: str, task: str) -> Dict:
144
+ """Index one element in the dataset.
145
+
146
+ Parameters
147
+ ----------
148
+ subject : str
149
+ The subject ID.
150
+ task : str
151
+ The task to get. Possible values are:
152
+ {"restingstate", "stopsignal", "emomatching", "workingmemory"}
153
+
154
+ Returns
155
+ -------
156
+ out : dict
157
+ Dictionary of paths for each type of data required for the
158
+ specified element.
159
+ """
160
+ out = super().get_item(subject=subject, task=task)
161
+ out["BOLD"]["mask_item"] = "BOLD_mask"
162
+ out["T1w"]["mask_item"] = "T1w_mask"
163
+ return out
@@ -51,6 +51,9 @@ def test_aomic1000_datagrabber() -> None:
51
51
  assert out["BOLD_confounds"]["path"].exists()
52
52
  assert out["BOLD_confounds"]["path"].is_file()
53
53
 
54
+ # assert BOLD_mask
55
+ assert out["BOLD_mask"]["path"].exists()
56
+
54
57
  # asserts type "T1w"
55
58
  assert "T1w" in out
56
59
 
@@ -63,6 +66,9 @@ def test_aomic1000_datagrabber() -> None:
63
66
  assert out["T1w"]["path"].exists()
64
67
  assert out["T1w"]["path"].is_file()
65
68
 
69
+ # asserts T1w_mask
70
+ assert out["T1w_mask"]["path"].exists()
71
+
66
72
  # asserts type "probseg_CSF"
67
73
  assert "probseg_CSF" in out
68
74
 
@@ -66,6 +66,9 @@ def test_aomic_piop1_datagrabber() -> None:
66
66
  assert out["BOLD_confounds"]["path"].exists()
67
67
  assert out["BOLD_confounds"]["path"].is_file()
68
68
 
69
+ # assert BOLD_mask
70
+ assert out["BOLD_mask"]["path"].exists()
71
+
69
72
  # asserts type "T1w"
70
73
  assert "T1w" in out
71
74
 
@@ -78,6 +81,9 @@ def test_aomic_piop1_datagrabber() -> None:
78
81
  assert out["T1w"]["path"].exists()
79
82
  assert out["T1w"]["path"].is_file()
80
83
 
84
+ # asserts T1w_mask
85
+ assert out["T1w_mask"]["path"].exists()
86
+
81
87
  # asserts type "probseg_CSF"
82
88
  assert "probseg_CSF" in out
83
89
 
@@ -35,7 +35,6 @@ def test_aomic_piop2_datagrabber() -> None:
35
35
 
36
36
  test_element = all_elements[0]
37
37
  sub, task = test_element
38
-
39
38
  out = dg[test_element]
40
39
 
41
40
  # asserts type "BOLD"
@@ -62,6 +61,9 @@ def test_aomic_piop2_datagrabber() -> None:
62
61
  assert out["BOLD_confounds"]["path"].exists()
63
62
  assert out["BOLD_confounds"]["path"].is_file()
64
63
 
64
+ # assert BOLD_mask
65
+ assert out["BOLD_mask"]["path"].exists()
66
+
65
67
  # asserts type "T1w"
66
68
  assert "T1w" in out
67
69
 
@@ -74,6 +76,9 @@ def test_aomic_piop2_datagrabber() -> None:
74
76
  assert out["T1w"]["path"].exists()
75
77
  assert out["T1w"]["path"].is_file()
76
78
 
79
+ # asserts T1w_mask
80
+ assert out["T1w_mask"]["path"].exists()
81
+
77
82
  # asserts type "probseg_CSF"
78
83
  assert "probseg_CSF" in out
79
84