Skip to content

Commit 5330cba

Browse files
authored
Merge pull request #11 from tclose/out-files
[WIP] Adds "out_files" output field to handle disambiguated postfixes
2 parents 8b05454 + 708f534 commit 5330cba

File tree

3 files changed

+96
-60
lines changed

3 files changed

+96
-60
lines changed

pydra/tasks/dcm2niix/utils.py

Lines changed: 72 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,63 @@
1+
import attrs
12
from pathlib import Path
23
from pydra import ShellCommandTask
34
from pydra.engine.specs import ShellSpec, ShellOutSpec, File, Directory, SpecInfo
45

56

6-
def dcm2niix_out_file(out_dir, filename, echo, suffix, compress):
7-
# Append echo number of NIfTI echo to select is provided
8-
if suffix:
9-
file_suffix = "_" + suffix
10-
elif echo:
11-
file_suffix = f"_e{echo}"
12-
else:
13-
file_suffix = ""
14-
15-
out_file = f"{out_dir}/{filename}{file_suffix}.nii"
16-
17-
# If compressed, append the zip extension
18-
if compress in ("y", "o", "i"):
19-
out_file += ".gz"
7+
def out_file_path(out_dir, filename, file_postfix, ext):
8+
"""Attempting to handle the different suffixes that are appended to filenames
9+
created by Dcm2niix (see https://github.com/rordenlab/dcm2niix/blob/master/FILENAMING.md)
10+
"""
2011

21-
out_file = Path(out_file).absolute()
12+
fpath = Path(out_dir) / (filename + (file_postfix if file_postfix else "") + ext)
13+
fpath = fpath.absolute()
2214

2315
# Check to see if multiple echos exist in the DICOM dataset
24-
if not out_file.exists():
25-
echoes = [
26-
str(p)
27-
for p in out_file.parent.iterdir()
28-
if p.stem.startswith(filename + "_e")
29-
]
30-
if echoes:
16+
if not fpath.exists():
17+
if file_postfix is not None: # NB: doesn't match attrs.NOTHING
18+
neighbours = [
19+
str(p) for p in fpath.parent.iterdir() if p.name.endswith(ext)
20+
]
3121
raise ValueError(
32-
"DICOM dataset contains multiple echos, please specify which "
33-
"echo you want via the 'echo' input:\n"
34-
"\n".join(echoes)
22+
f"\nDid not find expected file '{fpath}' (file_postfix={file_postfix}) "
23+
"after DICOM -> NIfTI conversion, please see "
24+
"https://github.com/rordenlab/dcm2niix/blob/master/FILENAMING.md for the "
25+
"list of postfixes that dcm2niix produces and provide an appropriate "
26+
"postfix, or set postfix to None to ignore matching a single file and use "
27+
"the list returned in 'out_files' instead. Found the following files "
28+
"with matching extensions:\n" + "\n".join(neighbours)
3529
)
30+
else:
31+
fpath = attrs.NOTHING # Did not find output path and
32+
33+
return fpath
3634

37-
return out_file
3835

36+
def dcm2niix_out_file(out_dir, filename, file_postfix, compress):
3937

40-
def dcm2niix_out_json(out_dir, filename, echo):
38+
ext = ".nii"
39+
# If compressed, append the zip extension
40+
if compress in ("y", "o", "i"):
41+
ext += ".gz"
42+
43+
return out_file_path(out_dir, filename, file_postfix, ext)
44+
45+
46+
def dcm2niix_out_json(out_dir, filename, file_postfix, bids):
4147
# Append echo number of NIfTI echo to select is provided
42-
if echo:
43-
echo_suffix = f"_e{echo}"
48+
if bids is attrs.NOTHING or bids in ("y", "o"):
49+
fpath = out_file_path(out_dir, filename, file_postfix, ".json")
4450
else:
45-
echo_suffix = ""
51+
fpath = attrs.NOTHING
52+
return fpath
53+
4654

47-
return Path(f"{out_dir}/{filename}{echo_suffix}.json").absolute()
55+
def dcm2niix_out_files(out_dir, filename):
56+
return [
57+
str(p.absolute())
58+
for p in Path(out_dir).iterdir()
59+
if p.name.startswith(filename)
60+
]
4861

4962

5063
input_fields = [
@@ -74,31 +87,19 @@ def dcm2niix_out_json(out_dir, filename, echo):
7487
{"argstr": "-f '{filename}'", "help_string": "The output name for the file"},
7588
),
7689
(
77-
"echo",
78-
int,
79-
{
80-
"argstr": "",
81-
"help_string": (
82-
"The echo number to extract from the DICOM dataset. When multiple "
83-
"echoes are discovered in the dataset then dcm2niix will create "
84-
"separate files for each echo with the suffix '_e<echo-number>.nii'"
85-
),
86-
"xor": ["suffix"],
87-
},
88-
),
89-
(
90-
"suffix",
90+
"file_postfix",
9191
str,
9292
{
93-
"argstr": "",
9493
"help_string": (
95-
"A suffix to append to the out_file, used to select which "
96-
"of the disambiguated outputs to return (see https://github.com/"
94+
"The postfix appended to the output filename. Used to select which "
95+
"of the disambiguated nifti files created by dcm2niix to return "
96+
"in this field (see https://github.com/"
9797
"rordenlab/dcm2niix/blob/master/FILENAMING.md"
98-
"#file-name-post-fixes-image-disambiguation) "
98+
"#file-name-post-fixes-image-disambiguation). Set to None to skip "
99+
"matching a single file (out_file will be set to attrs.NOTHING if the "
100+
"base path without postfixes doesn't exist) and handle the list of "
101+
"output files returned in 'out_files' instead."
99102
),
100-
"xor": ["echo"],
101-
"allowed_values": ["Eq", "ph", "imaginary", "MoCo", "real", "phMag"],
102103
},
103104
),
104105
(
@@ -350,7 +351,13 @@ def dcm2niix_out_json(out_dir, filename, echo):
350351
"out_file",
351352
File,
352353
{
353-
"help_string": "output NIfTI image",
354+
"help_string": (
355+
"output NIfTI image. If multiple nifti files are created (e.g. for "
356+
"different echoes), then the 'file_postfix' input can be provided to "
357+
"select which of them is considered the 'out_file'. Otherwise it "
358+
"should be set to None and 'out_files' used instead (in which case "
359+
"'out_file' will be set to attrs.NOTHING)",
360+
),
354361
"callable": dcm2niix_out_file,
355362
"mandatory": True,
356363
},
@@ -359,7 +366,7 @@ def dcm2niix_out_json(out_dir, filename, echo):
359366
"out_json",
360367
File,
361368
{
362-
"help_string": "output BIDS side-car JSON",
369+
"help_string": "output BIDS side-car JSON corresponding to 'out_file'",
363370
# "requires": [("bids", 'y')], FIXME: should be either 'y' or 'o'
364371
"callable": dcm2niix_out_json,
365372
},
@@ -380,6 +387,18 @@ def dcm2niix_out_json(out_dir, filename, echo):
380387
"output_file_template": "{out_dir}/{filename}.bvec",
381388
},
382389
),
390+
(
391+
"out_files",
392+
list,
393+
{
394+
"help_string": (
395+
"all output files in a list, including files disambiguated "
396+
"by their suffixes (e.g. echoes, phase-maps, etc... see "
397+
"https://github.com/rordenlab/dcm2niix/blob/master/FILENAMING.md"
398+
),
399+
"callable": dcm2niix_out_files,
400+
},
401+
),
383402
]
384403

385404
Dcm2NiixOutputSpec = SpecInfo(

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[build-system]
2+
requires = ["setuptools==62", "wheel"]

setup.cfg

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
[DEFAULT]
2-
subpackage = dcm2niix
3-
41
[metadata]
52
author = Thomas G. Close
63
author_email = [email protected]
@@ -28,8 +25,8 @@ packages = find_namespace:
2825

2926
[options.packages.find]
3027
include =
31-
pydra.tasks.%(subpackage)s
32-
pydra.tasks.%(subpackage)s.*
28+
pydra.tasks.dcm2niix
29+
pydra.tasks.dcm2niix.*
3330

3431
[options.extras_require]
3532
doc =
@@ -61,7 +58,25 @@ all =
6158
[versioneer]
6259
VCS = git
6360
style = pep440
64-
versionfile_source = pydra/tasks/%(subpackage)s/_version.py
65-
versionfile_build = pydra/tasks/%(subpackage)s/_version.py
61+
versionfile_source = pydra/tasks/dcm2niix/_version.py
62+
versionfile_build = pydra/tasks/dcm2niix/_version.py
6663
tag_prefix =
6764
parentdir_prefix =
65+
66+
[tool:pytest]
67+
addopts = --doctest-modules --doctest-report ndiff
68+
doctest_optionflags= NORMALIZE_WHITESPACE ELLIPSIS
69+
70+
[flake8]
71+
doctests = True
72+
exclude =
73+
**/__init__.py
74+
*build/
75+
pydra/tasks/dcm2niix/_version.py
76+
versioneer.py
77+
docs/source/conf.py
78+
max-line-length = 88
79+
select = C,E,F,W,B,B950
80+
extend-ignore = E203,E501
81+
per-file-ignores =
82+
setup.py:F401

0 commit comments

Comments
 (0)