From 17a0574d62a444702c5a3a573fd3ad0fcd7cfdac Mon Sep 17 00:00:00 2001 From: araikes Date: Tue, 2 Dec 2025 15:52:58 -0700 Subject: [PATCH 1/7] Improve interpolation description in boilerplate Refines the text describing interpolation methods in the boilerplate_from_eddy_config function. Now explicitly states the method used (Least-Squares Reconstruction or Jacobian modulation) and improves clarity of resampling steps. --- qsiprep/interfaces/eddy.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/qsiprep/interfaces/eddy.py b/qsiprep/interfaces/eddy.py index 36013fea..a4377441 100644 --- a/qsiprep/interfaces/eddy.py +++ b/qsiprep/interfaces/eddy.py @@ -417,14 +417,17 @@ def boilerplate_from_eddy_config(eddy_config, fieldmap_type, pepolar_method): ) # Format the interpolation - lsr_ref = ' [@fsllsr]' if ext_eddy.inputs.method == 'lsr' else '' if doing_2stage: desc.append( - 'Interpolation after head motion and initial susceptibility distortion correction' + 'Resampling after head motion and initial susceptibility ' + 'distortion correction was performed using ' ) else: - desc.append('Final interpolation') - desc.append(f'was performed using the `{ext_eddy.inputs.method}` method{lsr_ref}.') + desc.append('Final resamping was performed using ') + if ext_eddy.inputs.method == 'lsr': + desc.append('Least-Squares Reconstruction with spline interpolation ([@fsllsr]).') + else: + desc.append('Jacobian modulation with spline interpolation.') if not doing_2stage: desc.append('\n\n') return ' '.join(desc) From 20cb89cd9adf8ddfc18bd7e705d270ea683060d8 Mon Sep 17 00:00:00 2001 From: araikes Date: Wed, 3 Dec 2025 13:13:17 -0700 Subject: [PATCH 2/7] Move N4 description to workflow --- qsiprep/workflows/anatomical/volume.py | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/qsiprep/workflows/anatomical/volume.py b/qsiprep/workflows/anatomical/volume.py index 62091c9d..cc7aef3a 100644 --- a/qsiprep/workflows/anatomical/volume.py +++ b/qsiprep/workflows/anatomical/volume.py @@ -229,26 +229,11 @@ def init_anat_preproc_wf( workflow.add_nodes([inputnode]) return workflow - contrast = config.workflow.anat_modality[:-1] desc = """ #### Anatomical data preprocessing """ - desc += ( - f"""\ -A total of {num_anat_images} {contrast}-weighted ({contrast}w) images were found within the input -BIDS dataset. -All of them were corrected for intensity non-uniformity (INU) -using `N4BiasFieldCorrection` [@n4, ANTs {ANTS_VERSION}]. -""" - if num_anat_images > 1 - else f"""\ -The {contrast}-weighted ({contrast}w) image was corrected for intensity non-uniformity (INU) -using `N4BiasFieldCorrection` [@n4, ANTs {ANTS_VERSION}], -and used as an anatomical reference throughout the workflow. -""" - ) # Ensure there is 1 and only 1 anatomical reference anat_reference_wf = init_anat_template_wf(num_images=num_anat_images) @@ -589,6 +574,15 @@ def init_anat_template_wf(num_images) -> Workflow: num_images=num_images, ants_ver=BrainExtraction().version or '', ) + else: + workflow.__desc__ = f"""\ +The {contrast}-weighted ({contrast}) image was corrected for intensity non-uniformity (INU) +using `N4BiasFieldCorrection` [@n4, ANTs {ANTS_VERSION}], +and used as an anatomical reference throughout the workflow. +""".format( + contrast=config.workflow.anat_modality, + ants_ver=BrainExtraction().version or '', + ) inputnode = pe.Node(niu.IdentityInterface(fields=['images']), name='inputnode') outputnode = pe.Node( From 6f1c4d0a0653238ca3c739e03390a95a57b5f528 Mon Sep 17 00:00:00 2001 From: araikes Date: Wed, 3 Dec 2025 14:10:24 -0700 Subject: [PATCH 3/7] Update confounds workflow description for clarity Expanded and clarified the workflow description in init_dwi_confs_wf to provide more detail on the confounds calculated, including motion parameters, slice-level and image-level quality metrics, and preprocessing-related measures. --- qsiprep/workflows/dwi/confounds.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/qsiprep/workflows/dwi/confounds.py b/qsiprep/workflows/dwi/confounds.py index fd28682f..65d3537a 100644 --- a/qsiprep/workflows/dwi/confounds.py +++ b/qsiprep/workflows/dwi/confounds.py @@ -58,12 +58,18 @@ def init_dwi_confs_wf(): """ workflow = Workflow(name='dwi_confs_wf') workflow.__desc__ = """\ -Several confounding time-series were calculated based on the -preprocessed DWI: framewise displacement (FD) using the -implementation in *Nipype* [following the definitions by @power_fd_dvars]. -The head-motion estimates calculated in the correction step were also -placed within the corresponding confounds file. Slicewise cross correlation -was also calculated. +Several confounding time-series were calculated based on the preprocessed DWI. +Framewise displacement (FD) was computed from head-motion parameters using the +implementation in *Nipype* [following @power_fd_dvars]. The head-motion estimates +calculated in the correction step, including six rigid-body motion parameters +(three translations in mm, three rotations in radians) were also included in the +confounds file. Note that esimated motion parameters in the phase-encoding +direction have high undercertainty due to the effect of a constant (mean) +eddy-current field. Additional slice-level quality metrics (eddy standard +deviations) were included in the confounds file along with the original gradient +vectors, b-values, and image-level quality measures (mean intensity, b=0 mean, +b=0 correction). Preprocessing-related metrics (pre/post/change for denoising and +Gibbs unringing methods) were also included. """ inputnode = pe.Node( niu.IdentityInterface( From 6b694fd4cc9019a2607fbe2dfc0f0137e48312a7 Mon Sep 17 00:00:00 2001 From: araikes Date: Wed, 3 Dec 2025 14:11:43 -0700 Subject: [PATCH 4/7] Typo --- qsiprep/workflows/dwi/confounds.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qsiprep/workflows/dwi/confounds.py b/qsiprep/workflows/dwi/confounds.py index 65d3537a..897c0c64 100644 --- a/qsiprep/workflows/dwi/confounds.py +++ b/qsiprep/workflows/dwi/confounds.py @@ -63,7 +63,7 @@ def init_dwi_confs_wf(): implementation in *Nipype* [following @power_fd_dvars]. The head-motion estimates calculated in the correction step, including six rigid-body motion parameters (three translations in mm, three rotations in radians) were also included in the -confounds file. Note that esimated motion parameters in the phase-encoding +confounds file. Note that estimated motion parameters in the phase-encoding direction have high undercertainty due to the effect of a constant (mean) eddy-current field. Additional slice-level quality metrics (eddy standard deviations) were included in the confounds file along with the original gradient From 89c6d6b663dc58494dd935f77dd49bec2eefde89 Mon Sep 17 00:00:00 2001 From: araikes Date: Wed, 3 Dec 2025 15:00:21 -0700 Subject: [PATCH 5/7] Update anatomical boilerplate steps. Reorganized and updated the generation of workflow descriptions in init_anat_template_wf to provide more detailed and context-specific information about image reorientation, INU correction, and template creation. --- qsiprep/workflows/anatomical/volume.py | 77 +++++++++++++++++++------- 1 file changed, 57 insertions(+), 20 deletions(-) diff --git a/qsiprep/workflows/anatomical/volume.py b/qsiprep/workflows/anatomical/volume.py index cc7aef3a..e389792f 100644 --- a/qsiprep/workflows/anatomical/volume.py +++ b/qsiprep/workflows/anatomical/volume.py @@ -564,26 +564,6 @@ def init_anat_template_wf(num_images) -> Workflow: workflow = Workflow(name='anat_template_wf') contrast = config.workflow.anat_modality - if num_images > 1: - workflow.__desc__ = f"""\ -A {contrast}-reference map was computed after registration of -{num_images} {contrast} images (after INU-correction) using -`antsRegistration` [ANTs {ANTS_VERSION}; @ants]. -""".format( - contrast=config.workflow.anat_modality, - num_images=num_images, - ants_ver=BrainExtraction().version or '', - ) - else: - workflow.__desc__ = f"""\ -The {contrast}-weighted ({contrast}) image was corrected for intensity non-uniformity (INU) -using `N4BiasFieldCorrection` [@n4, ANTs {ANTS_VERSION}], -and used as an anatomical reference throughout the workflow. -""".format( - contrast=config.workflow.anat_modality, - ants_ver=BrainExtraction().version or '', - ) - inputnode = pe.Node(niu.IdentityInterface(fields=['images']), name='inputnode') outputnode = pe.Node( niu.IdentityInterface( @@ -601,6 +581,22 @@ def init_anat_template_wf(num_images) -> Workflow: omp_nthreads = config.nipype.omp_nthreads # 0. Reorient anatomical image(s) to LPS and resample to common voxel space + if num_images > 1: + desc = f"""\ +All {num_images} {contrast} images were + """.format( + contrast=config.workflow.anat_modality, + num_images=num_images, + ) + else: + desc = f"""\ +The {contrast} image was + """.format(contrast=config.workflow.anat_modality) + + desc += """\ + reoriented to LPS orientation and resampled to an isotropic voxel size using AFNI tools [@afni]. + """ + template_dimensions = pe.Node(TemplateDimensions(), name='template_dimensions') anat_conform = pe.MapNode( Conform(deoblique_header=True), iterfield='in_file', name='anat_conform' @@ -634,6 +630,16 @@ def init_anat_template_wf(num_images) -> Workflow: ) if num_images == 1: + desc += f"""\ +Intensity non-uniformity (INU) correction was applied to the {contrast} +image using ANTs' (ANTs version {ANTS_VERSION}; @ants) _N4BiasFieldCorrection_ (@n4) +algorithm with the following parameters: 50 iterations of convergence +at each level, a convergence threshold of 0.0000001, a shrink factor of 4, +and a B-spline fitting distance of 200 voxels. + """.format( + contrast=config.workflow.anat_modality, + ANTS_VERSION=BrainExtraction().version or '', + ) def _get_first(in_list): if isinstance(in_list, list | tuple): @@ -650,8 +656,22 @@ def _get_first(in_list): (n4_correct, outputnode, [('output_image', 'bias_corrected')]), ]) # fmt:skip + workflow.__desc__ = desc return workflow + # More than one image + desc += f"""\ +Intensity non-uniformity (INU) correction was applied to each {contrast} image +using ANTs' (ANTs version {ANTS_VERSION}; @ants) +_N4BiasFieldCorrection_ (@n4) algorithm with the following parameters: +50 iterations of convergence at each level, a convergence threshold +of 0.0000001, a shrink factor of 4, and a B-spline fitting distance +of 200 voxels. + """.format( + contrast=config.workflow.anat_modality, + ANTS_VERSION=BrainExtraction().version or '', + ) + # 1. Template (only if several images) # 1a. Correct for bias field: the bias field is an additive factor # in log-transformed intensity units. Therefore, it is not a linear @@ -663,6 +683,22 @@ def _get_first(in_list): # Make an unbiased template, same as used for b=0 registration align_to = config.workflow.subject_anatomical_reference + + if align_to == 'first-alphabetically': + desc += f"""\ +The INU-corrected {config.workflow.anat_modality} images were +then aligned using a rigid-body transformation to the first +image (alphabetically) as the reference using rigid body registration +in ANTs (@ants), and merged to create a single {config.workflow.anat_modality} template. + """ + else: + desc += f"""\ +The INU-corrected {config.workflow.anat_modality} images were +then aligned using a rigid-body transformation to an iteratively refined +template using rigid body registration in ANTs (@ants), and merged to create +a single {config.workflow.anat_modality} template. + """ + anat_merge_wf = init_b0_hmc_wf( align_to='first' if (align_to == 'first-alphabetically') else 'iterative', transform='Rigid', @@ -681,6 +717,7 @@ def _get_first(in_list): ]), ]) # fmt:skip + workflow.__desc__ = desc return workflow From 3e480d194b1e382ac1ff8a5cad900301102e0464 Mon Sep 17 00:00:00 2001 From: araikes Date: Wed, 3 Dec 2025 15:11:32 -0700 Subject: [PATCH 6/7] Better wording for reference images --- qsiprep/workflows/anatomical/volume.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/qsiprep/workflows/anatomical/volume.py b/qsiprep/workflows/anatomical/volume.py index e389792f..d04fdde8 100644 --- a/qsiprep/workflows/anatomical/volume.py +++ b/qsiprep/workflows/anatomical/volume.py @@ -635,7 +635,9 @@ def init_anat_template_wf(num_images) -> Workflow: image using ANTs' (ANTs version {ANTS_VERSION}; @ants) _N4BiasFieldCorrection_ (@n4) algorithm with the following parameters: 50 iterations of convergence at each level, a convergence threshold of 0.0000001, a shrink factor of 4, -and a B-spline fitting distance of 200 voxels. +and a B-spline fitting distance of 200 voxels. This imaged served as the +anatomical reference for subsequent steps. +\n\n """.format( contrast=config.workflow.anat_modality, ANTS_VERSION=BrainExtraction().version or '', @@ -689,14 +691,17 @@ def _get_first(in_list): The INU-corrected {config.workflow.anat_modality} images were then aligned using a rigid-body transformation to the first image (alphabetically) as the reference using rigid body registration -in ANTs (@ants), and merged to create a single {config.workflow.anat_modality} template. +in ANTs (@ants), and merged to create a single {config.workflow.anat_modality} +reference image. +\n\n """ else: desc += f"""\ The INU-corrected {config.workflow.anat_modality} images were then aligned using a rigid-body transformation to an iteratively refined template using rigid body registration in ANTs (@ants), and merged to create -a single {config.workflow.anat_modality} template. +a single {config.workflow.anat_modality} reference image. +\n\n """ anat_merge_wf = init_b0_hmc_wf( From 48671631976f6ed86009e377588100bc26fb5911 Mon Sep 17 00:00:00 2001 From: araikes Date: Wed, 3 Dec 2025 15:42:00 -0700 Subject: [PATCH 7/7] Improve PF Gibbs description and citation --- qsiprep/workflows/dwi/merge.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/qsiprep/workflows/dwi/merge.py b/qsiprep/workflows/dwi/merge.py index 3aae8e0c..f97652ba 100644 --- a/qsiprep/workflows/dwi/merge.py +++ b/qsiprep/workflows/dwi/merge.py @@ -561,7 +561,11 @@ def get_buffernode(): n_procs=omp_nthreads, ) elif unringing_method == 'rpg': - desc += f'{last_step}Gibbs ringing was removed using TORTOISE [@pfgibbs]. ' + desc += ( + f'{last_step}partial-Fourier induced Gibbs ringing was ' + 'removed using the approach implemented in TORTOISE v4 ' + '[@tortoisev4; @pfgibbs]. ' + ) pe_code = { 'i': 0,