diff --git a/src/rbc/core/common.py b/src/rbc/core/common.py index b7c1b97a..559a1885 100644 --- a/src/rbc/core/common.py +++ b/src/rbc/core/common.py @@ -10,13 +10,14 @@ from typing import TYPE_CHECKING import nibabel as nib -from niwrap import afni, fsl +from niwrap import afni if TYPE_CHECKING: from collections.abc import Sequence from pathlib import Path from rbc.core.fileops import file_tmp_copy +from rbc.core.niwrap import generate_exec_folder __all__ = ["deoblique_and_reorient", "merge_3d_to_4d", "split_4d"] @@ -49,19 +50,24 @@ def deoblique_and_reorient( def split_4d(img_4d: Path) -> list[Path]: """Split a 4D NIfTI timeseries into individual 3D volumes. + Volumes are written as uncompressed NIfTI (.nii) to avoid gzip + overhead on float intermediates that are read back immediately. + Args: img_4d: Path to a 4D NIfTI image. Returns: Sorted list of paths to the individual 3D volume files. """ - split_result = fsl.fslsplit( - infile=img_4d, separation_time=True, output_basename="vol_" - ) - assert split_result.out_files is not None # noqa: S101 - out_files = split_result.out_files - out_dir = out_files[0].parent if isinstance(out_files, list) else out_files.parent - return sorted(out_dir.glob("vol_*.nii.gz")) + img = nib.nifti1.load(img_4d) + volumes = nib.four_to_three(img) + out_dir = generate_exec_folder(suffix="split4d") + paths: list[Path] = [] + for idx, vol in enumerate(volumes): + out_path = out_dir / f"vol_{idx:04d}.nii" + nib.save(vol, out_path) + paths.append(out_path) + return paths def merge_3d_to_4d(volumes: Sequence[Path], output: Path) -> Path: diff --git a/src/rbc/core/functional/distortion.py b/src/rbc/core/functional/distortion.py index f2529664..2e2c5a30 100644 --- a/src/rbc/core/functional/distortion.py +++ b/src/rbc/core/functional/distortion.py @@ -349,7 +349,7 @@ def correct_distortion_phasediff( diff_img = nib.Nifti1Image( diff_data, affine=p1_img.affine, header=p1_img.header ) - phasediff = out_dir / "phasediff.nii.gz" + phasediff = out_dir / "phasediff.nii" nib.save(diff_img, phasediff) # 4. Prepare fieldmap (rad/s) @@ -454,7 +454,7 @@ def correct_distortion_pepolar( # 2. Merge forward/reverse into 4D merged_path = merge_3d_to_4d( volumes=[epi_forward, epi_reverse], - output=out_dir / "merged_epi.nii.gz", + output=out_dir / "merged_epi.nii", ) # 3. Estimate field with TOPUP diff --git a/src/rbc/core/functional/masking.py b/src/rbc/core/functional/masking.py index 20e18421..e19e4a19 100644 --- a/src/rbc/core/functional/masking.py +++ b/src/rbc/core/functional/masking.py @@ -183,7 +183,7 @@ def bold_masking( warped_probseg = ants.ants_apply_transforms( reference_image=bold_ref, output=ants.ants_apply_transforms_warped_output( - warped_output_file_name="probseg_transform.nii.gz" + warped_output_file_name="probseg_transform.nii" ), default_value=0, float_=True, @@ -222,7 +222,7 @@ def bold_masking( # mismatches before N4 correction. bold_ref_dir_corrected = ants.set_direction_by_matrix( infile=bold_ref, - outfile="bold_ref_dir_corrected.nii.gz", + outfile="bold_ref_dir_corrected.nii", direction_matrix=parse_direction_matrix_from_header( dilated_binary_mask.output_file ), @@ -233,7 +233,7 @@ def bold_masking( n4_bias_correction = ants.n4_bias_field_correction( input_image=bold_ref_dir_corrected.outfile, output=ants.n4_bias_field_correction_corrected_output( - corrected_output_file_name="ref_bold_corrected.nii.gz" + corrected_output_file_name="ref_bold_corrected.nii" ), image_dimensionality=3, bspline_fitting=ants.n4_bias_field_correction_bspline_fitting( @@ -286,7 +286,7 @@ def bold_masking( in_file=masked_bold.output_file, cl_frac=0.2, rbt=[18.3, 65, 90], - prefix="uni.nii.gz", + prefix="uni.nii", t2=True, ) diff --git a/src/rbc/core/functional/motion.py b/src/rbc/core/functional/motion.py index 391887d6..31ecbaa2 100644 --- a/src/rbc/core/functional/motion.py +++ b/src/rbc/core/functional/motion.py @@ -80,7 +80,7 @@ def extract_motion_reference(in_file: Path) -> Path: header=ref_im.header, ) - temp_slice_file = generate_exec_folder(suffix="motion_ref_input") / "slice.nii.gz" + temp_slice_file = generate_exec_folder(suffix="motion_ref_input") / "slice.nii" ref_im.to_filename(temp_slice_file) mc_output_prefix = f"{_MC_PREFIX}_volreg.nii.gz" diff --git a/src/rbc/core/functional/resampling.py b/src/rbc/core/functional/resampling.py index 742a23bb..dc596542 100644 --- a/src/rbc/core/functional/resampling.py +++ b/src/rbc/core/functional/resampling.py @@ -99,7 +99,7 @@ def apply_motion_transforms( default_value=0, dimensionality=3, output=ants.ants_apply_transforms_warped_output( - f"vol_{idx:04d}_motion.nii.gz" + f"vol_{idx:04d}_motion.nii" ), ) transformed_vols.append(result.output.output_image_outfile) @@ -193,7 +193,7 @@ def resample_bold_to_template( default_value=0, dimensionality=3, output=ants.ants_apply_transforms_warped_output( - f"vol_{idx:04d}_template.nii.gz" + f"vol_{idx:04d}_template.nii" ), ) transformed_vols.append(result.output.output_image_outfile) diff --git a/src/rbc/core/longitudinal/transform.py b/src/rbc/core/longitudinal/transform.py index 37bf2514..1289ee34 100644 --- a/src/rbc/core/longitudinal/transform.py +++ b/src/rbc/core/longitudinal/transform.py @@ -137,7 +137,7 @@ def _transform_4d_chunked(in_file: Path, template: Path, xfm: Path) -> Path: dimensionality=3, interpolation=ants.ants_apply_transforms_linear(), output=ants.ants_apply_transforms_warped_output( - f"vol_{idx:04d}_template.nii.gz" + f"vol_{idx:04d}_template.nii" ), ) transformed_vols.append(result.output.output_image_outfile)