Skip to content

Commit 932e1e3

Browse files
authored
Merge pull request #547 from effigies/rf/multisource_derivatives
RF: Enable DerivativesDataSink to take multiple source files to derive entities
2 parents 5767039 + e66217a commit 932e1e3

File tree

1 file changed

+34
-4
lines changed

1 file changed

+34
-4
lines changed

niworkflows/interfaces/bids.py

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,8 @@ class _DerivativesDataSinkInputSpec(DynamicTraitedSpec, BaseInterfaceInputSpec):
279279
File(exists=True), mandatory=True, desc="the object to be saved"
280280
)
281281
meta_dict = traits.DictStrAny(desc="an input dictionary containing metadata")
282-
source_file = File(exists=False, mandatory=True, desc="the input func file")
282+
source_file = InputMultiObject(
283+
File(exists=False), mandatory=True, desc="the source file(s) to extract entities from")
283284

284285

285286
class _DerivativesDataSinkOutputSpec(TraitedSpec):
@@ -348,6 +349,32 @@ class DerivativesDataSink(SimpleInterface):
348349
['.../niworkflows/sub-01/ses-retest/anat/sub-01_ses-retest_custom1-1_custom2-b_T1w.nii',
349350
'.../niworkflows/sub-01/ses-retest/anat/sub-01_ses-retest_custom1-2_custom2-b_T1w.nii']
350351
352+
When multiple source files are passed, only common entities are passed down.
353+
For example, if two T1w images from different sessions are used to generate
354+
a single image, the session entity is removed automatically.
355+
356+
>>> bids_dir = tmpdir / 'bidsroot'
357+
>>> multi_source = [
358+
... bids_dir / 'sub-02/ses-A/anat/sub-02_ses-A_T1w.nii.gz',
359+
... bids_dir / 'sub-02/ses-B/anat/sub-02_ses-B_T1w.nii.gz']
360+
>>> for source_file in multi_source:
361+
... source_file.parent.mkdir(parents=True, exist_ok=True)
362+
... _ = source_file.write_text("")
363+
>>> dsink = DerivativesDataSink(base_directory=str(tmpdir), check_hdr=False)
364+
>>> dsink.inputs.in_file = str(tmpfile)
365+
>>> dsink.inputs.source_file = list(map(str, multi_source))
366+
>>> dsink.inputs.desc = 'preproc'
367+
>>> res = dsink.run()
368+
>>> res.outputs.out_file # doctest: +ELLIPSIS
369+
'.../niworkflows/sub-02/anat/sub-02_desc-preproc_T1w.nii'
370+
371+
If, on the other hand, only one is used, the session is preserved:
372+
373+
>>> dsink.inputs.source_file = str(multi_source[0])
374+
>>> res = dsink.run()
375+
>>> res.outputs.out_file # doctest: +ELLIPSIS
376+
'.../niworkflows/sub-02/ses-A/anat/sub-02_ses-A_desc-preproc_T1w.nii'
377+
351378
>>> bids_dir = tmpdir / 'bidsroot' / 'sub-02' / 'ses-noanat' / 'func'
352379
>>> bids_dir.mkdir(parents=True, exist_ok=True)
353380
>>> tricky_source = bids_dir / 'sub-02_ses-noanat_task-rest_run-01_bold.nii.gz'
@@ -480,9 +507,12 @@ def _run_interface(self, runtime):
480507
self._metadata = meta
481508

482509
# Initialize entities with those from the source file.
483-
out_entities = parse_file_entities(
484-
str(relative_to_root(self.inputs.source_file))
485-
)
510+
in_entities = [
511+
parse_file_entities(str(relative_to_root(source_file)))
512+
for source_file in self.inputs.source_file
513+
]
514+
out_entities = {k: v for k, v in in_entities[0].items()
515+
if all(ent.get(k) == v for ent in in_entities[1:])}
486516
for drop_entity in listify(self.inputs.dismiss_entities or []):
487517
out_entities.pop(drop_entity, None)
488518

0 commit comments

Comments
 (0)