[ENH]: Add support for native space #252

Merged
synchon merged 13 commits from feat/native-space-support into main 2023-10-27 09:49:49 +00:00
8 changed files with 133 additions and 17 deletions

View file

@ -0,0 +1 @@
Add ``native_t1w`` parameter to :class:`.DataladAOMICID1000`, :class:`.DataladAOMICPIOP1`, :class:`.DataladAOMICPIOP2`, enabling fetching of T1w data in subject-native space by `Synchon Mandal`_

View file

@ -0,0 +1 @@
Add support for subject-native space by `Synchon Mandal`_ and `Fede Raimondo`_

View file

@ -28,6 +28,8 @@ class DataladAOMICID1000(PatternDataladDataGrabber):
"probseg_WM", "DWI"} or a list of the options, optional "probseg_WM", "DWI"} or a list of the options, optional
AOMIC data types. If None, all available data types are selected. AOMIC data types. If None, all available data types are selected.
(default None). (default None).
native_t1w : bool, optional
Whether to use T1w in native space (default False).
""" """
@ -35,6 +37,7 @@ class DataladAOMICID1000(PatternDataladDataGrabber):
self, self,
datadir: Union[str, Path, None] = None, datadir: Union[str, Path, None] = None,
types: Union[str, List[str], None] = None, types: Union[str, List[str], None] = None,
native_t1w: bool = False,
) -> None: ) -> None:
# The patterns # The patterns
patterns = { patterns = {
@ -84,6 +87,27 @@ class DataladAOMICID1000(PatternDataladDataGrabber):
"sub-{subject}_desc-preproc_dwi.nii.gz" "sub-{subject}_desc-preproc_dwi.nii.gz"
), ),
} }
# Use native T1w assets
self.native_t1w = False
if native_t1w:
self.native_t1w = True
patterns.update(
{
"T1w": (
"derivatives/fmriprep/sub-{subject}/anat/"
"sub-{subject}_desc-preproc_T1w.nii.gz"
),
"T1w_mask": (
"derivatives/fmriprep/sub-{subject}/anat/"
"sub-{subject}_desc-brain_mask.nii.gz"
),
"Warp": (
"derivatives/fmriprep/sub-{subject}/anat/"
"sub-{subject}_from-MNI152NLin2009cAsym_to-T1w_"
"mode-image_xfm.h5"
),
}
)
# Set default types # Set default types
if types is None: if types is None:
types = list(patterns.keys()) types = list(patterns.keys())
@ -126,5 +150,8 @@ class DataladAOMICID1000(PatternDataladDataGrabber):
if out.get("T1w"): if out.get("T1w"):
out["T1w"]["mask_item"] = "T1w_mask" out["T1w"]["mask_item"] = "T1w_mask"
# Add space information # Add space information
if self.native_t1w:
out["T1w"].update({"space": "native"}) out["T1w"].update({"space": "native"})
else:
out["T1w"].update({"space": "MNI152NLin2009cAsym"})
return out return out

View file

@ -34,6 +34,8 @@ class DataladAOMICPIOP1(PatternDataladDataGrabber):
"gstroop", "workingmemory"} or list of the options, optional "gstroop", "workingmemory"} or list of the options, optional
AOMIC PIOP1 task sessions. If None, all available task sessions are AOMIC PIOP1 task sessions. If None, all available task sessions are
selected (default None). selected (default None).
native_t1w : bool, optional
Whether to use T1w in native space (default False).
""" """
@ -42,6 +44,7 @@ class DataladAOMICPIOP1(PatternDataladDataGrabber):
datadir: Union[str, Path, None] = None, datadir: Union[str, Path, None] = None,
types: Union[str, List[str], None] = None, types: Union[str, List[str], None] = None,
tasks: Union[str, List[str], None] = None, tasks: Union[str, List[str], None] = None,
native_t1w: bool = False,
) -> None: ) -> None:
# Declare all tasks # Declare all tasks
all_tasks = [ all_tasks = [
@ -114,6 +117,27 @@ class DataladAOMICPIOP1(PatternDataladDataGrabber):
"sub-{subject}_desc-preproc_dwi.nii.gz" "sub-{subject}_desc-preproc_dwi.nii.gz"
), ),
} }
# Use native T1w assets
self.native_t1w = False
if native_t1w:
self.native_t1w = True
patterns.update(
{
"T1w": (
"derivatives/fmriprep/sub-{subject}/anat/"
"sub-{subject}_desc-preproc_T1w.nii.gz"
),
"T1w_mask": (
"derivatives/fmriprep/sub-{subject}/anat/"
"sub-{subject}_desc-brain_mask.nii.gz"
),
"Warp": (
"derivatives/fmriprep/sub-{subject}/anat/"
"sub-{subject}_from-MNI152NLin2009cAsym_to-T1w_"
"mode-image_xfm.h5"
),
}
)
# Set default types # Set default types
if types is None: if types is None:
types = list(patterns.keys()) types = list(patterns.keys())
@ -171,7 +195,10 @@ class DataladAOMICPIOP1(PatternDataladDataGrabber):
if out.get("T1w"): if out.get("T1w"):
out["T1w"]["mask_item"] = "T1w_mask" out["T1w"]["mask_item"] = "T1w_mask"
# Add space information # Add space information
if self.native_t1w:
out["T1w"].update({"space": "native"}) out["T1w"].update({"space": "native"})
else:
out["T1w"].update({"space": "MNI152NLin2009cAsym"})
return out return out
def get_elements(self) -> List: def get_elements(self) -> List:

View file

@ -34,6 +34,8 @@ class DataladAOMICPIOP2(PatternDataladDataGrabber):
or list of the options, optional or list of the options, optional
AOMIC PIOP2 task sessions. If None, all available task sessions are AOMIC PIOP2 task sessions. If None, all available task sessions are
selected (default None). selected (default None).
native_t1w : bool, optional
Whether to use T1w in native space (default False).
""" """
@ -42,6 +44,7 @@ class DataladAOMICPIOP2(PatternDataladDataGrabber):
datadir: Union[str, Path, None] = None, datadir: Union[str, Path, None] = None,
types: Union[str, List[str], None] = None, types: Union[str, List[str], None] = None,
tasks: Union[str, List[str], None] = None, tasks: Union[str, List[str], None] = None,
native_t1w: bool = False,
) -> None: ) -> None:
# Declare all tasks # Declare all tasks
all_tasks = [ all_tasks = [
@ -111,6 +114,27 @@ class DataladAOMICPIOP2(PatternDataladDataGrabber):
"sub-{subject}_desc-preproc_dwi.nii.gz" "sub-{subject}_desc-preproc_dwi.nii.gz"
), ),
} }
# Use native T1w assets
self.native_t1w = False
if native_t1w:
self.native_t1w = True
patterns.update(
{
"T1w": (
"derivatives/fmriprep/sub-{subject}/anat/"
"sub-{subject}_desc-preproc_T1w.nii.gz"
),
"T1w_mask": (
"derivatives/fmriprep/sub-{subject}/anat/"
"sub-{subject}_desc-brain_mask.nii.gz"
),
"Warp": (
"derivatives/fmriprep/sub-{subject}/anat/"
"sub-{subject}_from-MNI152NLin2009cAsym_to-T1w_"
"mode-image_xfm.h5"
),
}
)
# Set default types # Set default types
if types is None: if types is None:
types = list(patterns.keys()) types = list(patterns.keys())
@ -171,5 +195,8 @@ class DataladAOMICPIOP2(PatternDataladDataGrabber):
if out.get("T1w"): if out.get("T1w"):
out["T1w"]["mask_item"] = "T1w_mask" out["T1w"]["mask_item"] = "T1w_mask"
# Add space information # Add space information
if self.native_t1w:
out["T1w"].update({"space": "native"}) out["T1w"].update({"space": "native"})
else:
out["T1w"].update({"space": "MNI152NLin2009cAsym"})
return out return out

View file

@ -98,7 +98,7 @@ class HCP1200(PatternDataGrabber):
) )
suffix = "_hp2000_clean" if ica_fix else "" suffix = "_hp2000_clean" if ica_fix else ""
# The types of data # The types of data
types = ["BOLD"] types = ["BOLD", "T1w", "Warp"]
# The patterns # The patterns
patterns = { patterns = {
"BOLD": ( "BOLD": (
@ -106,7 +106,9 @@ class HCP1200(PatternDataGrabber):
"{task}_{phase_encoding}/" "{task}_{phase_encoding}/"
"{task}_{phase_encoding}" "{task}_{phase_encoding}"
f"{suffix}.nii.gz" f"{suffix}.nii.gz"
) ),
"T1w": "{subject}/T1w/T1w_acpc_dc_restore.nii.gz",
"Warp": "{subject}/MNINonLinear/xfms/standard2acpc_dc.nii.gz",
} }
# The replacements # The replacements
replacements = ["subject", "task", "phase_encoding"] replacements = ["subject", "task", "phase_encoding"]
@ -147,6 +149,12 @@ class HCP1200(PatternDataGrabber):
out = super().get_item( out = super().get_item(
subject=subject, task=new_task, phase_encoding=phase_encoding subject=subject, task=new_task, phase_encoding=phase_encoding
) )
# Add space for BOLD data type
if "BOLD" in out:
out["BOLD"].update({"space": "MNI152NLin6Asym"})
# Add space for T1w data type
if "T1w" in out:
out["T1w"].update({"space": "native"})
return out return out
def get_elements(self) -> List: def get_elements(self) -> List:

View file

@ -104,6 +104,10 @@ class DefaultDataReader(PipelineStepMixin, UpdateMetaMixin):
params = {} params = {}
# For each type of data, try to read it # For each type of data, try to read it
for type_ in input.keys(): for type_ in input.keys():
# Skip Warp data type
if type_ == "Warp":
continue
# Check for malformed datagrabber specification # Check for malformed datagrabber specification
if "path" not in input[type_]: if "path" not in input[type_]:
warn_with_log( warn_with_log(

View file

@ -4,8 +4,8 @@
# License: AGPL # License: AGPL
from itertools import product from itertools import product
from tempfile import TemporaryDirectory
from pathlib import Path from pathlib import Path
from tempfile import TemporaryDirectory
import datalad.api as dl import datalad.api as dl
@ -15,7 +15,6 @@ DST = "git@gin.g-node.org:/juaml/datalad-example-hcp1200.git"
if __name__ == "__main__": if __name__ == "__main__":
with TemporaryDirectory() as tmpdir: with TemporaryDirectory() as tmpdir:
# Convert str to Path # Convert str to Path
tmpdir_path = Path(tmpdir) tmpdir_path = Path(tmpdir)
@ -29,7 +28,30 @@ if __name__ == "__main__":
# Create subject directory # Create subject directory
subdir.mkdir() subdir.mkdir()
for (task, phase_encoding) in product( # T1w data
# Set subject data directory
sub_t1w_datadir = subdir / "T1w"
# Create subject data directory
sub_t1w_datadir.mkdir(parents=True)
# Set subject data file
sub_t1w_datafile = sub_t1w_datadir / "T1w_acpc_dc_restore.nii.gz"
# Write subject data file
with open(sub_t1w_datafile, "w") as f:
f.write("placeholder")
# Warp data
# Set subject data directory
sub_warp_datadir = subdir / "MNINonLinear" / "xfms"
# Create subject data directory
sub_warp_datadir.mkdir(parents=True)
# Set subject data file
sub_warp_datafile = sub_warp_datadir / "standard2acpc_dc.nii.gz"
# Write subject data file
with open(sub_warp_datafile, "w") as f:
f.write("placeholder")
# BOLD data
for task, phase_encoding in product(
[ [
"REST1", "REST1",
"REST2", "REST2",
@ -49,32 +71,31 @@ if __name__ == "__main__":
else: else:
new_task = f"tfMRI_{task}" new_task = f"tfMRI_{task}"
# Set subject data directory # Set subject data directory
sub_datadir = ( sub_bold_datadir = (
subdir subdir
/ "MNINonLinear" / "MNINonLinear"
/ "Results" / "Results"
/ f"{new_task}_{phase_encoding}" / f"{new_task}_{phase_encoding}"
) )
# Create subject data directory # Create subject data directory
sub_datadir.mkdir(parents=True) sub_bold_datadir.mkdir(parents=True)
# Set subject data file # Set subject data file
sub_datafile = ( sub_bold_datafile = (
sub_datadir sub_bold_datadir / f"{new_task}_{phase_encoding}.nii.gz"
/ f"{new_task}_{phase_encoding}.nii.gz"
) )
# Create subject data file # Create subject data file
with open(sub_datafile, "w") as f: with open(sub_bold_datafile, "w") as f:
f.write("placeholder") f.write("placeholder")
if "REST" in task: if "REST" in task:
# Set subject data file with ICA+FIX # Set subject data file with ICA+FIX
sub_datafile = ( sub_bold_datafile = (
sub_datadir sub_bold_datadir
/ f"{new_task}_{phase_encoding}_hp2000_clean.nii.gz" / f"{new_task}_{phase_encoding}_hp2000_clean.nii.gz"
) )
# Create subject data file with ICA+FIX # Create subject data file with ICA+FIX
with open(sub_datafile, "w") as f: with open(sub_bold_datafile, "w") as f:
f.write("placeholder") f.write("placeholder")
# Save datalad dataset # Save datalad dataset