Skip to content

Commit 65e0e92

Browse files
committed
defacing with template automated, need to add mni & average pet flags/options next
1 parent 7aa3881 commit 65e0e92

File tree

7 files changed

+167
-141
lines changed

7 files changed

+167
-141
lines changed

data/participants.tsv

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
participant_id height weight age gender
2-
sub-01 178 58 28 male
2+
sub-01 178 58 28 male
3+
sub-mni305 200 65 10 n/a
3.13 MB
Binary file not shown.

petdeface/noanat.py

Lines changed: 65 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -10,39 +10,41 @@
1010

1111
def get_data_path(filename: str) -> Path:
1212
"""Get the path to a data file included in the package.
13-
13+
1414
Parameters
1515
----------
1616
filename : str
1717
Name of the file to get the path for. This should be relative to the data directory.
1818
For example: "sub-01/ses-baseline/anat/sub-01_ses-baseline_T1w.nii"
19-
19+
2020
Returns
2121
-------
2222
Path
2323
Path to the requested data file
24-
24+
2525
Raises
2626
------
2727
FileNotFoundError
2828
If the requested file is not found in the package data
2929
"""
3030
# Get the path to the data directory
3131
data_dir = Path(__file__).parent.parent / "data"
32-
32+
3333
# Construct the full path
3434
full_path = data_dir / filename
35-
35+
3636
# Check if the file exists
3737
if not full_path.exists():
38-
raise FileNotFoundError(f"Could not find data file {filename} in data directory")
39-
38+
raise FileNotFoundError(
39+
f"Could not find data file {filename} in data directory"
40+
)
41+
4042
return full_path
4143

4244

4345
def get_default_anat() -> Path:
4446
"""Get the path to the default anatomical image.
45-
47+
4648
Returns
4749
-------
4850
Path
@@ -54,7 +56,7 @@ def get_default_anat() -> Path:
5456

5557
def get_default_anat_data() -> nibabel.Nifti1Image:
5658
"""Get the default anatomical image as a nibabel image object.
57-
59+
5860
Returns
5961
-------
6062
nibabel.Nifti1Image
@@ -65,56 +67,58 @@ def get_default_anat_data() -> nibabel.Nifti1Image:
6567

6668
def extract_subject_id(input_str: str) -> str:
6769
"""Extract subject ID from various input formats.
68-
70+
6971
This function can handle:
7072
- Full paths (e.g., "/path/to/sub-123/anat/file.nii")
7173
- Subject IDs with prefix (e.g., "sub-123")
7274
- Raw subject IDs (e.g., "123")
73-
75+
7476
Parameters
7577
----------
7678
input_str : str
7779
Input string containing a subject ID in any format
78-
80+
7981
Returns
8082
-------
8183
str
8284
Extracted subject ID without the 'sub-' prefix
83-
85+
8486
Raises
8587
------
8688
ValueError
8789
If no valid subject ID can be extracted
8890
"""
8991
# Pattern to match 'sub-XXXX' in various contexts
9092
# The pattern captures everything after 'sub-' until it hits a non-alphanumeric character or the end of the string
91-
pattern = r'sub-([a-zA-Z0-9]+)(?:_|$)'
92-
93+
pattern = r"sub-([a-zA-Z0-9]+)(?:_|$)"
94+
9395
# Try to find a match
9496
match = re.search(pattern, input_str)
95-
97+
9698
if match:
9799
# Return the captured group (the subject ID without 'sub-' prefix)
98100
return match.group(1)
99101
else:
100102
# If no 'sub-' prefix is found, check if the input is a valid subject ID
101-
if re.match(r'^[a-zA-Z0-9]+$', input_str):
103+
if re.match(r"^[a-zA-Z0-9]+$", input_str):
102104
return input_str
103105
else:
104106
# Try a more flexible approach for paths
105-
path_match = re.search(r'sub-([a-zA-Z0-9]+)', input_str)
107+
path_match = re.search(r"sub-([a-zA-Z0-9]+)", input_str)
106108
if path_match:
107109
return path_match.group(1)
108110
else:
109-
raise ValueError(f"Could not extract a valid subject ID from '{input_str}'")
111+
raise ValueError(
112+
f"Could not extract a valid subject ID from '{input_str}'"
113+
)
110114

111115

112116
def copy_default_anat_to_subject(bids_dir: Union[str, Path], subject_id: str) -> dict:
113117
"""Copy the default anatomical image to a PET subject's folder in a BIDS dataset.
114-
118+
115119
This function extracts the subject ID from the provided string using regex,
116120
then copies the default anatomical image and its JSON metadata to the subject's folder in the BIDS dataset.
117-
121+
118122
Parameters
119123
----------
120124
bids_dir : Union[str, Path]
@@ -124,7 +128,7 @@ def copy_default_anat_to_subject(bids_dir: Union[str, Path], subject_id: str) ->
124128
- Full path (e.g., "/path/to/sub-123/anat/file.nii")
125129
- Subject ID with prefix (e.g., "sub-123")
126130
- Raw subject ID (e.g., "123")
127-
131+
128132
Returns
129133
-------
130134
dict
@@ -135,7 +139,7 @@ def copy_default_anat_to_subject(bids_dir: Union[str, Path], subject_id: str) ->
135139
'created_dirs': List of paths to newly created directories,
136140
'created_files': List of paths to newly created files
137141
}
138-
142+
139143
Raises
140144
------
141145
FileNotFoundError
@@ -145,71 +149,71 @@ def copy_default_anat_to_subject(bids_dir: Union[str, Path], subject_id: str) ->
145149
"""
146150
# Convert bids_dir to Path if it's a string
147151
bids_dir = Path(bids_dir)
148-
152+
149153
# Check if the BIDS directory exists
150154
if not bids_dir.exists():
151155
raise FileNotFoundError(f"BIDS directory {bids_dir} does not exist")
152-
156+
153157
# Extract subject ID using regex
154158
try:
155159
extracted_id = extract_subject_id(subject_id)
156160
except ValueError as e:
157161
raise ValueError(f"Invalid subject ID: {e}")
158-
162+
159163
# Create the subject directory structure
160164
subject_dir = bids_dir / f"sub-{extracted_id}"
161-
165+
162166
# Check if the subject directory exists
163167
if not subject_dir.exists():
164168
raise FileNotFoundError(f"Subject directory {subject_dir} does not exist")
165-
169+
166170
anat_dir = subject_dir / "anat"
167-
171+
168172
# Create the anatomical directory if it doesn't exist
169173
created_dirs = []
170174
if not anat_dir.exists():
171175
anat_dir.mkdir(parents=True, exist_ok=True)
172176
created_dirs.append(anat_dir)
173-
177+
174178
# Define the target file paths
175179
target_nii = anat_dir / f"sub-{extracted_id}_T1w.nii"
176180
target_json = anat_dir / f"sub-{extracted_id}_T1w.json"
177-
181+
178182
# Get the source file paths
179183
source_nii = get_default_anat()
180184
source_json = get_data_path("sub-01/ses-baseline/anat/sub-01_ses-baseline_T1w.json")
181-
185+
182186
# Copy the files
183187
created_files = []
184188
shutil.copy2(source_nii, target_nii)
185189
created_files.append(target_nii)
186-
190+
187191
shutil.copy2(source_json, target_json)
188192
created_files.append(target_json)
189-
193+
190194
print(f"Copied default anatomical image to {target_nii}")
191195
print(f"Copied default anatomical metadata to {target_json}")
192-
196+
193197
# Return information about created files and directories
194198
return {
195-
'subject_dir': subject_dir,
196-
'anat_dir': anat_dir,
197-
'created_dirs': created_dirs,
198-
'created_files': created_files
199+
"subject_dir": subject_dir,
200+
"anat_dir": anat_dir,
201+
"created_dirs": created_dirs,
202+
"created_files": created_files,
199203
}
200204

201205

202206
def remove_default_anat(
203-
bids_dir: Union[str, Path],
204-
subject_id: Optional[str] = None,
205-
created_items: Optional[Dict] = None
207+
bids_dir: Union[str, Path],
208+
subject_id: Optional[str] = None,
209+
created_items: Optional[Dict] = None,
206210
) -> None:
207211
"""Remove the default anatomical image and directory created for a subject.
208-
212+
209213
This function can be used in two ways:
210214
1. With the bids_dir and subject_id to identify what to remove
211215
2. With the bids_dir and the dictionary returned by copy_default_anat_to_subject
212-
216+
213217
Parameters
214218
----------
215219
bids_dir : Union[str, Path]
@@ -218,11 +222,11 @@ def remove_default_anat(
218222
Subject ID in any format (if created_items is not provided)
219223
created_items : Optional[Dict]
220224
Dictionary returned by copy_default_anat_to_subject (if subject_id is not provided)
221-
225+
222226
Returns
223227
-------
224228
None
225-
229+
226230
Raises
227231
------
228232
ValueError
@@ -232,68 +236,67 @@ def remove_default_anat(
232236
"""
233237
# Convert bids_dir to Path if it's a string
234238
bids_dir = Path(bids_dir)
235-
239+
236240
# Check if the BIDS directory exists
237241
if not bids_dir.exists():
238242
raise FileNotFoundError(f"BIDS directory {bids_dir} does not exist")
239-
243+
240244
# Determine which approach to use
241245
if subject_id is not None and created_items is not None:
242246
raise ValueError("Cannot provide both subject_id and created_items")
243247
elif subject_id is None and created_items is None:
244248
raise ValueError("Must provide either subject_id or created_items")
245-
249+
246250
if created_items is not None:
247251
# Use the dictionary returned by copy_default_anat_to_subject
248-
anat_dir = created_items['anat_dir']
249-
created_files = created_items['created_files']
250-
created_dirs = created_items['created_dirs']
252+
anat_dir = created_items["anat_dir"]
253+
created_files = created_items["created_files"]
254+
created_dirs = created_items["created_dirs"]
251255
else:
252256
# Extract subject ID using regex
253257
try:
254258
extracted_id = extract_subject_id(subject_id)
255259
except ValueError as e:
256260
raise ValueError(f"Invalid subject ID: {e}")
257-
261+
258262
# Create the subject directory structure
259263
subject_dir = bids_dir / f"sub-{extracted_id}"
260-
264+
261265
# Check if the subject directory exists
262266
if not subject_dir.exists():
263267
raise FileNotFoundError(f"Subject directory {subject_dir} does not exist")
264-
268+
265269
anat_dir = subject_dir / "anat"
266-
270+
267271
# Define the file paths
268272
target_nii = anat_dir / f"sub-{extracted_id}_T1w.nii"
269273
target_json = anat_dir / f"sub-{extracted_id}_T1w.json"
270-
274+
271275
# Check if the files exist
272276
created_files = []
273277
if target_nii.exists():
274278
created_files.append(target_nii)
275279
if target_json.exists():
276280
created_files.append(target_json)
277-
281+
278282
# Check if the directory exists and is empty
279283
created_dirs = []
280284
if anat_dir.exists():
281285
# Only add to created_dirs if it's empty or only contains our files
282286
remaining_files = set(anat_dir.iterdir()) - set(created_files)
283287
if not remaining_files:
284288
created_dirs.append(anat_dir)
285-
289+
286290
# Remove the files
287291
for file_path in created_files:
288292
if file_path.exists():
289293
file_path.unlink()
290294
print(f"Removed file: {file_path}")
291-
295+
292296
# Remove the directories (only if they're empty)
293297
for dir_path in created_dirs:
294298
if dir_path.exists() and not any(dir_path.iterdir()):
295299
dir_path.rmdir()
296300
print(f"Removed directory: {dir_path}")
297-
298-
print("Cleanup completed successfully")
299301

302+
print("Cleanup completed successfully")

0 commit comments

Comments
 (0)