Skip to content

Commit 029b954

Browse files
authored
updated workflow to support individual subject selection (#44)
Workflow now supports selection of single or mulitple subjects --subject flag and --excludesubject flag is also now available for use. Minor fixes: * update docs * update rtd build * update lock and pyproject.toml
1 parent 76f8298 commit 029b954

File tree

6 files changed

+1816
-1351
lines changed

6 files changed

+1816
-1351
lines changed

.readthedocs.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ build:
1515
- pip install poetry
1616
- poetry config virtualenvs.create false
1717
post_install:
18-
- poetry install
18+
- VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH poetry install --with=dev
1919

2020
# Build documentation in the "docs/" directory with Sphinx
2121
sphinx:

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ This software can be installed via source or via pip from PyPi with `pip install
3838
usage: petdeface.py [-h] [--output_dir OUTPUT_DIR] [--anat_only]
3939
[--subject SUBJECT] [--session SESSION] [--docker]
4040
[--n_procs N_PROCS] [--skip_bids_validator] [--version]
41-
[--placement PLACEMENT] [--remove_existing] input_dir
41+
[--placement PLACEMENT] [--remove_existing] [--excludesubject]
42+
input_dir
4243

4344
PetDeface
4445

@@ -65,6 +66,8 @@ options:
6566
w/ defaced at input_dir
6667
'derivatives': does all of the defacing within the derivatives folder in input_dir.
6768
--remove_existing, -r Remove existing output files in output_dir.
69+
--excludesubject EXCLUDESUBJECT [EXCLUDESUBJECT ...]
70+
Exclude a subject(s) from the defacing workflow. e.g. --excludesubject sub-01 sub-02
6871
```
6972

7073
Working example usage:

docs/usage.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ Additional options can be found in the help menu::
113113
e.g. replaces faced PET and T1w images w/ defaced at input_dir'derivatives': does all of the defacing within the derivatives folder in input_dir.
114114
--remove_existing, -r
115115
Remove existing output files in output_dir.
116+
--excludesubject EXCLUDESUBJECT [EXCLUDESUBJECT ...]
117+
Exclude a subject(s) from the defacing workflow. e.g. --excludesubject sub-01 sub-02
116118

117119
Docker Based
118120
------------

petdeface/petdeface.py

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -201,12 +201,42 @@ def deface(args: Union[dict, argparse.Namespace]) -> None:
201201
if not check_valid_fs_license():
202202
raise Exception("You need a valid FreeSurfer license to proceed!")
203203

204-
if args.participant_label:
205-
participants = [args.participant_label]
206-
else:
204+
if args.subject:
205+
subjects = args.subject
206+
# if subject contains the string sub-, remove it to avoid redundancy as pybids will add it uses the
207+
# right side of the sub- string as the subject label
208+
if any("sub-" in subject for subject in subjects):
209+
print("One or more subject contains sub- string")
210+
subjects = [
211+
subject.replace("sub-", "") for subject in subjects if "sub-" in subject
212+
]
213+
# raise error if a supplied subject is not contained in the dataset
207214
participants = collect_participants(
208215
args.bids_dir, bids_validate=~args.skip_bids_validator
209216
)
217+
for subject in subjects:
218+
if subject not in participants:
219+
raise FileNotFoundError(
220+
f"sub-{subject} not found in dataset {args.bids_dir}"
221+
)
222+
else:
223+
subjects = collect_participants(
224+
args.bids_dir, bids_validate=~args.skip_bids_validator
225+
)
226+
227+
# check to see if any subjects are excluded from the defacing workflow
228+
if args.excludesubject != []:
229+
print(
230+
f"Removing the following subjects {args.excludesubject} from the defacing workflow"
231+
)
232+
args.excludesubject = [
233+
subject.replace("sub-", "") for subject in args.excludesubject
234+
]
235+
subjects = [
236+
subject for subject in subjects if subject not in args.excludesubject
237+
]
238+
239+
print(f"Subjects remaining in the defacing workflow: {subjects}")
210240

211241
# clean up and create derivatives directories
212242
if args.output_dir == "None" or args.output_dir is None:
@@ -216,7 +246,7 @@ def deface(args: Union[dict, argparse.Namespace]) -> None:
216246

217247
petdeface_wf = Workflow(name="petdeface_wf", base_dir=output_dir)
218248

219-
for subject_id in participants:
249+
for subject_id in subjects:
220250
try:
221251
single_subject_wf = init_single_subject_wf(
222252
subject_id, args.bids_dir, preview_pics=args.preview_pics
@@ -268,6 +298,8 @@ def init_single_subject_wf(
268298

269299
data = collect_anat_and_pet(bids_data)
270300
subject_data = data.get(subject_id)
301+
if subject_data is None:
302+
raise FileNotFoundError(f"Could not find data for subject sub-{subject_id}")
271303

272304
# check if any t1w images exist for the pet images
273305
for pet_image, t1w_image in subject_data.items():
@@ -672,6 +704,7 @@ def __init__(
672704
remove_existing=True, # TODO: currently not implemented
673705
placement="adjacent", # TODO: currently not implemented
674706
preview_pics=True,
707+
excludesubject=[],
675708
):
676709
self.bids_dir = bids_dir
677710
self.remove_existing = remove_existing
@@ -683,6 +716,7 @@ def __init__(
683716
self.n_procs = n_procs
684717
self.skip_bids_validator = skip_bids_validator
685718
self.preview_pics = preview_pics
719+
self.excludesubject = excludesubject
686720

687721
# check if freesurfer license is valid
688722
self.fs_license = check_valid_fs_license()
@@ -707,6 +741,7 @@ def run(self):
707741
"placement": self.placement,
708742
"remove_existing": self.remove_existing,
709743
"preview_pics": self.preview_pics,
744+
"excludesubject": self.excludesubject,
710745
}
711746
)
712747
wrap_up_defacing(
@@ -746,6 +781,7 @@ def cli():
746781
"-s",
747782
help="The label of the subject to be processed.",
748783
type=str,
784+
nargs="+",
749785
required=False,
750786
default="",
751787
)
@@ -800,6 +836,14 @@ def cli():
800836
action="store_true",
801837
default=False,
802838
)
839+
parser.add_argument(
840+
"--excludesubject",
841+
help="Exclude a subject(s) from the defacing workflow. e.g. --excludesubject sub-01 sub-02",
842+
type=str,
843+
nargs="+",
844+
required=False,
845+
default=[],
846+
)
803847

804848
arguments = parser.parse_args()
805849
return arguments
@@ -928,6 +972,7 @@ def main(): # noqa: max-complexity: 12
928972
remove_existing=args.remove_existing,
929973
placement=args.placement,
930974
preview_pics=args.preview_pics,
975+
excludesubject=args.excludesubject,
931976
)
932977
petdeface.run()
933978

0 commit comments

Comments
 (0)