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) diff --git a/qsiprep/workflows/anatomical/volume.py b/qsiprep/workflows/anatomical/volume.py index 62091c9d..d04fdde8 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) @@ -579,17 +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 '', - ) - inputnode = pe.Node(niu.IdentityInterface(fields=['images']), name='inputnode') outputnode = pe.Node( niu.IdentityInterface( @@ -607,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' @@ -640,6 +630,18 @@ 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. This imaged served as the +anatomical reference for subsequent steps. +\n\n + """.format( + contrast=config.workflow.anat_modality, + ANTS_VERSION=BrainExtraction().version or '', + ) def _get_first(in_list): if isinstance(in_list, list | tuple): @@ -656,8 +658,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 @@ -669,6 +685,25 @@ 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} +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} reference image. +\n\n + """ + anat_merge_wf = init_b0_hmc_wf( align_to='first' if (align_to == 'first-alphabetically') else 'iterative', transform='Rigid', @@ -687,6 +722,7 @@ def _get_first(in_list): ]), ]) # fmt:skip + workflow.__desc__ = desc return workflow diff --git a/qsiprep/workflows/dwi/confounds.py b/qsiprep/workflows/dwi/confounds.py index fd28682f..897c0c64 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 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 +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( 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,