oceanproc 1.2.0__tar.gz → 1.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (21) hide show
  1. {oceanproc-1.2.0 → oceanproc-1.2.2}/PKG-INFO +1 -1
  2. {oceanproc-1.2.0 → oceanproc-1.2.2}/oceanproc/bids_wrapper.py +51 -24
  3. {oceanproc-1.2.0 → oceanproc-1.2.2}/oceanproc/ocean_proc.py +4 -1
  4. {oceanproc-1.2.0 → oceanproc-1.2.2}/oceanproc/preprocessing_wrapper.py +23 -11
  5. {oceanproc-1.2.0 → oceanproc-1.2.2}/pyproject.toml +1 -1
  6. {oceanproc-1.2.0 → oceanproc-1.2.2}/README.md +0 -0
  7. {oceanproc-1.2.0 → oceanproc-1.2.2}/oceanproc/__init__.py +0 -0
  8. {oceanproc-1.2.0 → oceanproc-1.2.2}/oceanproc/events_long.py +0 -0
  9. {oceanproc-1.2.0 → oceanproc-1.2.2}/oceanproc/firstlevel/__init__.py +0 -0
  10. {oceanproc-1.2.0 → oceanproc-1.2.2}/oceanproc/firstlevel/run_glm.py +0 -0
  11. {oceanproc-1.2.0 → oceanproc-1.2.2}/oceanproc/firstlevel/tests/__init__.py +0 -0
  12. {oceanproc-1.2.0 → oceanproc-1.2.2}/oceanproc/firstlevel/tests/test_run_glm.py +0 -0
  13. {oceanproc-1.2.0 → oceanproc-1.2.2}/oceanproc/group_series.py +0 -0
  14. {oceanproc-1.2.0 → oceanproc-1.2.2}/oceanproc/grouplevel/__init__.py +0 -0
  15. {oceanproc-1.2.0 → oceanproc-1.2.2}/oceanproc/grouplevel/computations.py +0 -0
  16. {oceanproc-1.2.0 → oceanproc-1.2.2}/oceanproc/grouplevel/design_mat_utils.py +0 -0
  17. {oceanproc-1.2.0 → oceanproc-1.2.2}/oceanproc/grouplevel/dscalar_utils.py +0 -0
  18. {oceanproc-1.2.0 → oceanproc-1.2.2}/oceanproc/grouplevel/utils.py +0 -0
  19. {oceanproc-1.2.0 → oceanproc-1.2.2}/oceanproc/oceanparse.py +0 -0
  20. {oceanproc-1.2.0 → oceanproc-1.2.2}/oceanproc/segmentation_wrapper.py +0 -0
  21. {oceanproc-1.2.0 → oceanproc-1.2.2}/oceanproc/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: oceanproc
3
- Version: 1.2.0
3
+ Version: 1.2.2
4
4
  Summary: MRI preprocessing for WUSTL Ocean labs
5
5
  Author-Email: Ramone Agard <rhagard@wustl.edu>, Joey Scanga <joeyscanga92@gmail.com>
6
6
  Requires-Python: <3.13,>=3.10
@@ -182,19 +182,6 @@ def run_dcm2niix(source_dir:Path,
182
182
  exit_program_early(f"Problem running '{title}'.",
183
183
  exit_func=clean_up_func if clean_up_func else None)
184
184
 
185
- # Delete or move extra files from short runs
186
- files_to_remove = list(tmp_nifti_dir.glob("*a.nii.gz")) + list(tmp_nifti_dir.glob("*a.json")) + list(tmp_nifti_dir.glob(f"10[0-9][0-9]_{source_dir.name}*"))
187
- if flags.debug:
188
- unused_files_dir = tmp_nifti_dir.parent / f"{tmp_nifti_dir.name}_unused"
189
- unused_files_dir.mkdir(exist_ok=True)
190
- logger.debug(f"moving some unused files and files created from shortened runs to directory {unused_files_dir}")
191
- for f in files_to_remove:
192
- shutil.move(f.resolve(), (unused_files_dir / f.name).resolve())
193
- else:
194
- logger.info(f"removing some unused files and files created from shortened runs :\n {[str(f) for f in files_to_remove]}")
195
- for f in files_to_remove:
196
- os.remove(f)
197
-
198
185
 
199
186
  def get_usability_from_file(usability_file:Path):
200
187
  quality_pairs = {}
@@ -310,7 +297,8 @@ def dicom_to_bids(subject:str,
310
297
  nifti:bool = False,
311
298
  skip_validate:bool = False,
312
299
  skip_prompt:bool = False,
313
- session_index:int = 0):
300
+ session_index:int = 0,
301
+ keep_all_niftis:bool = False):
314
302
  """
315
303
  Facilitates the conversion of DICOM data into NIFTI data in BIDS format, and the removal of data marked 'unusable'.
316
304
 
@@ -346,32 +334,71 @@ def dicom_to_bids(subject:str,
346
334
  else:
347
335
  return
348
336
 
349
- tmp_path = bids_dir / f"tmp_dcm2bids/sub-{subject}_ses-{session}"
337
+ tmp_nifti_path = bids_dir / f"tmp_dcm2bids/sub-{subject}_ses-{session}"
350
338
 
351
339
  def clean_up(quiet=False):
352
340
  try:
353
- logger.debug(f"removing the temporary directory used by dcm2bids: {tmp_path}")
354
- shutil.rmtree(tmp_path)
341
+ logger.debug(f"removing the temporary directory used by dcm2bids: {tmp_nifti_path}")
342
+ shutil.rmtree(tmp_nifti_path)
355
343
  except Exception:
356
344
  if not quiet:
357
- logger.warning(f"There was a problem deleting the temporary directory at {tmp_path}")
345
+ logger.warning(f"There was a problem deleting the temporary directory at {tmp_nifti_path}")
358
346
 
359
- nifti_path = None
360
347
  clean_up(quiet=True)
361
348
  if not nifti:
362
349
  run_dcm2niix(source_dir=source_dir,
363
- tmp_nifti_dir=tmp_path)
364
- nifti_path = tmp_path
350
+ tmp_nifti_dir=tmp_nifti_path)
365
351
  else:
366
- nifti_path = source_dir
352
+ # copy current niftis to tmp_nifti_path
353
+ logger.info(f"copying the source NIFTI files to tmp directory, {tmp_nifti_path}")
354
+ shutil.copytree(source_dir.resolve(), tmp_nifti_path.resolve())
355
+
356
+
357
+ # filter out know 'weird' files that can be created during conversion
358
+ if not keep_all_niftis:
359
+ files_to_remove = []
360
+ short_runs = list(tmp_nifti_path.glob("[!.]*[a-z].nii*")) + list(tmp_nifti_path.glob("[!.]*[a-z].json"))
361
+ for sr in short_runs:
362
+ suffix = None
363
+ if sr.name.endswith(".nii.gz"):
364
+ suffix = ".nii.gz"
365
+ elif sr.name.endswith(".nii"):
366
+ suffix = ".nii"
367
+ elif sr.name.endswith(".json"):
368
+ suffix = ".json"
369
+ else:
370
+ continue
371
+ if (sr.parent / f"{sr.name.removesuffix(suffix)[:-1]}{suffix}").exists():
372
+ files_to_remove.append(sr)
373
+
374
+ for jf in tmp_nifti_path.glob("[!.]*.json"):
375
+ with open(jf, "r") as f:
376
+ jd = json.load(f)
377
+ if int(jd["SeriesNumber"]) > 1000:
378
+ files_to_remove.extend(list(tmp_nifti_path.glob(f"{jf.stem}.*")))
379
+
380
+ if len(files_to_remove) > 0:
381
+ logger.info("filtering out NIFTI files")
382
+ file_list_string = '\n\t'.join([str(f) for f in files_to_remove])
383
+ if flags.debug:
384
+ unused_files_dir = tmp_nifti_path.parent / f"{tmp_nifti_path.name}_unused"
385
+ unused_files_dir.mkdir(exist_ok=True)
386
+ logger.debug(f"moving some unused files and files created from shortened runs to directory {unused_files_dir}:\n\t{file_list_string}")
387
+ for f in files_to_remove:
388
+ shutil.move(f.resolve(), (unused_files_dir / f.name).resolve())
389
+ else:
390
+ file_list_string = '\n\t'.join([str(f) for f in files_to_remove])
391
+ logger.info(f"removing some unused files and files created from shortened runs :\n\t{file_list_string}")
392
+ for f in files_to_remove:
393
+ os.remove(f)
367
394
 
368
- add_information_to_sidecar(nifti_dir=nifti_path,
395
+ add_information_to_sidecar(nifti_dir=tmp_nifti_path,
369
396
  usability_file=usability_file,
370
397
  session_number=session_index)
371
398
 
372
399
  run_dcm2bids(subject=subject,
373
400
  session=session,
374
- nifti_dir=nifti_path,
401
+ nifti_dir=tmp_nifti_path,
375
402
  bids_output_dir=bids_dir,
376
403
  config_file=bids_config,
377
404
  nordic_config=nordic_config,
@@ -121,6 +121,8 @@ def main():
121
121
  help="The path to the second dcm2bids config file to use for this subject and session. This implies that the session contains NORDIC data")
122
122
  config_args.add_argument("--nifti", action=argparse.BooleanOptionalAction,
123
123
  help="Flag to specify that the source directory contains files of type NIFTI (.nii/.jsons) instead of DICOM")
124
+ config_args.add_argument("--keep_all_niftis", action=argparse.BooleanOptionalAction,
125
+ help="Oceanproc will try its best to remove 'weird' NIFTI files that can be introduced during conversion from DICOM. This flag specifies that oceanproc should not do that and keep all of the files instead.")
124
126
  config_args.add_argument("--anat_only", action=argparse.BooleanOptionalAction,
125
127
  help="Flag to specify only anatomical images should be processed.")
126
128
  config_args.add_argument("--fd_spike_threshold", "-fd", type=float, default=0.9,
@@ -258,7 +260,8 @@ def main():
258
260
  nifti=args.nifti,
259
261
  skip_validate=args.skip_bids_validation,
260
262
  skip_prompt=index > 0,
261
- session_index=index
263
+ session_index=index,
264
+ keep_all_niftis=args.keep_all_niftis
262
265
  )
263
266
  if index > 0:
264
267
  extend_session(subject=args.subject,
@@ -76,9 +76,14 @@ def run_preprocessing(subject:str,
76
76
  exit_program_early(f"Bids path {bids_path} does not exist.")
77
77
  elif not derivs_path.exists():
78
78
  exit_program_early(f"Derivatives path {derivs_path} does not exist.")
79
+
80
+ license_mount = "/opt/freesurfer/license.txt"
81
+ bids_mount = "/data"
82
+ derivs_mount = "/out"
83
+ work_mount = "/work"
79
84
 
80
85
  if flags.apptainer:
81
- cmd_prelude = "apptainer run --nv --cleanenv --no-mount cwd"
86
+ cmd_prelude = f"apptainer run --nv --cleanenv --no-mount cwd --pwd {work_mount}"
82
87
  mount_flag = "-B"
83
88
  else:
84
89
  uid = Popen(["id", "-u"], stdout=PIPE).stdout.read().decode("utf-8").strip()
@@ -104,10 +109,6 @@ def run_preprocessing(subject:str,
104
109
  additional_mount_paths = " ".join(additional_mount_paths)
105
110
  additional_mount_args = " ".join(additional_mount_args)
106
111
 
107
- license_mount = "/opt/freesurfer/license.txt"
108
- bids_mount = "/data"
109
- derivs_mount = "/out"
110
- work_mount = "/work"
111
112
  preproc_command = f"""{cmd_prelude}
112
113
  {mount_flag} {license_file.resolve()}:{license_mount}:ro
113
114
  {mount_flag} {bids_path.resolve()}:{bids_mount}:ro
@@ -256,16 +257,27 @@ def insert_dummy_frames(subject:str,
256
257
  for bfile in func_files:
257
258
  if not Path(bfile.path).exists:
258
259
  continue
260
+
259
261
  # Find the dscans file for bold run
262
+ try_find = True
260
263
  dscans_entities = bids_layout.parse_file_entities(bfile.path) | {"suffix":dscans_suffix, "extension":".tsv"}
261
- dscans_bids_path = bids_layout.build_path(dscans_entities, path_patterns=[dscans_path_pattern], validate=False)
262
- dscans_file = sorted(dscan_dir.glob(Path(dscans_bids_path).name))
263
- if len(dscans_file) > 1:
264
- exit_program_early(f"Found more than one dscans file for bold run {bfile} --> {dscans_file}")
265
- elif len(dscans_file) == 0:
264
+ while try_find:
265
+ dscans_bids_path = bids_layout.build_path(dscans_entities, path_patterns=[dscans_path_pattern], validate=False)
266
+ dscans_file = sorted(dscan_dir.glob(Path(dscans_bids_path).name))
267
+ if len(dscans_file) > 1:
268
+ exit_program_early(f"Found more than one dscans file for bold run {bfile} --> {dscans_file}")
269
+ elif len(dscans_file) == 1:
270
+ try_find = False
271
+ if ("echo" in dscans_entities) and try_find:
272
+ del dscans_entities["echo"]
273
+ else:
274
+ try_find = False
275
+
276
+ if len(dscans_file) == 0:
266
277
  logger.info(f"Did not find any dscans files for bold run: {bfile}, file will remain unmodified.")
267
278
  continue
268
- dscans_file = dscans_file[0]
279
+ else:
280
+ dscans_file = dscans_file[0]
269
281
  logger.info(f"found dscans file <{dscans_file.name}> for bold run <{bfile.filename}>")
270
282
 
271
283
  dscans_list = pd.read_csv(dscans_file, sep="\t").loc[:, "dummy_scan"].to_numpy().astype(np.int32)
@@ -34,7 +34,7 @@ dependencies = [
34
34
  "pybids>=0.16.3",
35
35
  ]
36
36
  name = "oceanproc"
37
- version = "1.2.0"
37
+ version = "1.2.2"
38
38
  description = "MRI preprocessing for WUSTL Ocean labs"
39
39
  readme = "README.md"
40
40
 
File without changes
File without changes