Skip to content

Commit 7cdd0fe

Browse files
mgxdeffigies
andauthored
ENH: Precomputed derivatives (#173)
* WIP: Anatomical derivatives * RF: Ensure derivatives are not selected in existing queries * ENH: Allow the use of precalculated derivatives * STY: black * FIX: Syntax errors, remove anatomical derivatives * ENH: Port `--derivatives` to wrapper script * ENH: Fast-track if precomputed mask is available * ENH: Fast-track if precomputed aseg is available * STY: black * DOC: Add section on `--derivatives` usage * Apply suggestions from code review Co-authored-by: Chris Markiewicz <[email protected]> * FIX: Remove session restriction for now, add info log * FIX: Remove duplicate connection * ENH: Add flag to control derivatives filtering * ENH: Add filtering options to wrapper arguments * STY: black this is the final straw * FIX: Remove excess print * Update nibabies/utils/bids.py Co-authored-by: Chris Markiewicz <[email protected]> Co-authored-by: Chris Markiewicz <[email protected]>
1 parent 9e31599 commit 7cdd0fe

File tree

10 files changed

+410
-251
lines changed

10 files changed

+410
-251
lines changed

docs/faqs.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,29 @@
11
# Tips and FAQs
22

3-
## Providing your own segmentations
3+
## Leveraging precomputed results
4+
5+
Whether to allow for manual intervention for tough cases, or simply to reduce processing time, *NiBabies* can allow the use of certain pre-computed files during processing.
6+
Initial support is limited to the following files:
7+
- Anatomical mask in T1w space
8+
- Antomical segmentation (aseg) in T1w space
9+
10+
To use pre-computed results, one or more [BIDS Derivatives](https://bids-specification.readthedocs.io/en/stable/05-derivatives/01-introduction.html#bids-derivatives) directories must be passed in to *NiBabies* using the `--derivatives` flag.
11+
Derivative directories must include a [`dataset_description.json` and the required fields](https://bids-specification.readthedocs.io/en/stable/03-modality-agnostic-files.html#derived-dataset-and-pipeline-description).
12+
Additionally, files must include the `space-orig` key-value pair in the name.
13+
14+
A sample layout of a derivatives directory can be found below:
15+
16+
```
17+
my_precomputed/
18+
├── dataset_description.json
19+
└── sub-01
20+
└── anat
21+
├── sub-01_space-orig_desc-aseg_dseg.nii.gz
22+
├── sub-01_space-orig_desc-brain_mask.json
23+
└── sub-01_space-orig_desc-brain_mask.nii.gz
24+
```
25+
26+
## Multi-atlas segmentation with joint label fusion
427

528
By default, *NiBabies* will run [FSL FAST](https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/FAST) for tissue segmentation, and Infant FreeSurfer for segmentation labels.
629
However, you can instead use ANTs Joint Label Fusion to generate both, granted you provide multiple atlases with anatomicals / segmentations via the `--segmentation-atlases-dir` flag.

nibabies/cli/parser.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -625,7 +625,21 @@ def _slice_time_ref(value, parser):
625625
"--fd-radius",
626626
type=float,
627627
default=45,
628-
help="Head radius in mm for framewise displacement calculation",
628+
help="Head radius in mm for framewise displacement calculation.",
629+
)
630+
g_baby.add_argument(
631+
"-d",
632+
"--derivatives",
633+
type=PathExists,
634+
nargs="+",
635+
help="One or more directory containing pre-computed derivatives.",
636+
)
637+
g_baby.add_argument(
638+
"--deriv-filter-file",
639+
dest="derivatives_filters",
640+
type=_bids_filter,
641+
metavar="FILE",
642+
help="A JSON file for customizing the derivatives queries.",
629643
)
630644
return parser
631645

@@ -758,7 +772,7 @@ def parse_args(args=None, namespace=None):
758772

759773
# Force initialization of the BIDSLayout
760774
config.execution.init()
761-
all_subjects = config.execution.layout.get_subjects()
775+
all_subjects = config.execution.layout.get_subjects(scope="raw")
762776
if config.execution.participant_label is None:
763777
config.execution.participant_label = all_subjects
764778

nibabies/config.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,10 @@ class execution(_Config):
356356
"""Run in sloppy mode (meaning, suboptimal parameters that minimize run-time)."""
357357
debug = []
358358
"""Debug mode(s)."""
359+
derivatives = None
360+
"""One or more paths where pre-computed derivatives are found."""
361+
derivatives_filters = None
362+
"""A dictionary of BIDS selection filters"""
359363
echo_idx = None
360364
"""Select a particular echo for multi-echo EPI datasets."""
361365
fs_license_file = _fs_license
@@ -451,18 +455,25 @@ def init(cls):
451455
database_path=_db_path,
452456
reset_database=cls.bids_database_dir is None,
453457
indexer=_indexer,
458+
derivatives=cls.derivatives or False,
454459
)
455460
cls.bids_database_dir = _db_path
456461
cls.layout = cls._layout
457-
if cls.bids_filters:
462+
463+
def _unserialize_bids_queries(queries):
458464
from bids.layout import Query
459465

460-
# unserialize pybids Query enum values
461-
for acq, filters in cls.bids_filters.items():
462-
cls.bids_filters[acq] = {
466+
for acq, filters in queries.items():
467+
queries[acq] = {
463468
k: getattr(Query, v[7:-4]) if not isinstance(v, Query) and "Query" in v else v
464469
for k, v in filters.items()
465470
}
471+
return queries
472+
473+
if cls.bids_filters:
474+
cls.bids_filters = _unserialize_bids_queries(cls.bids_filters)
475+
if cls.derivatives_filters:
476+
cls.derivatives_filters = _unserialize_bids_queries(cls.derivatives_filters)
466477

467478
if "all" in cls.debug:
468479
cls.debug = list(DEBUG_MODES)

nibabies/utils/bids.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ def group_bolds_ref(*, layout, subject):
158158

159159
for ses, suffix in sorted(
160160
product(
161-
layout.get_sessions(subject=subject) or (None,),
161+
layout.get_sessions(subject=subject, scope="raw") or (None,),
162162
{
163163
"bold",
164164
},
@@ -304,3 +304,46 @@ def validate_input_dir(exec_env, bids_dir, participant_label):
304304
subprocess.check_call(["bids-validator", str(bids_dir), "-c", temp.name])
305305
except FileNotFoundError:
306306
print("bids-validator does not appear to be installed", file=sys.stderr)
307+
308+
309+
def collect_precomputed_derivatives(layout, subject_id, derivatives_filters=None):
310+
"""
311+
Query and collect precomputed derivatives.
312+
313+
This function is used to determine which workflow steps can be skipped,
314+
based on the files found.
315+
"""
316+
317+
deriv_queries = {
318+
'anat_mask': {
319+
'datatype': 'anat',
320+
'desc': 'brain',
321+
'space': 'orig',
322+
'suffix': 'mask',
323+
},
324+
'anat_aseg': {
325+
'datatype': 'anat',
326+
'desc': 'aseg',
327+
'space': 'orig',
328+
'suffix': 'dseg',
329+
},
330+
}
331+
if derivatives_filters is not None:
332+
deriv_queries.update(derivatives_filters)
333+
334+
derivatives = {}
335+
for deriv, query in deriv_queries.items():
336+
res = layout.get(
337+
scope='derivatives',
338+
subject=subject_id,
339+
extension=['.nii', '.nii.gz'],
340+
**query,
341+
)
342+
if not res:
343+
continue
344+
if len(res) > 1: # Some queries may want multiple results
345+
raise Exception(
346+
f"When searching for <{deriv}>, found multiple results: {[f.path for f in res]}"
347+
)
348+
derivatives[deriv] = res[0]
349+
return derivatives

0 commit comments

Comments
 (0)