QSIPrep transform segmentations from original T1 space to pre-processed space

Hello there,

I ran qsiprep-0.16.1 on my dataset. It ran :ok::+1:
:white_check_mark: Here is my output data structure :

├── anat
│   ├── sub-VS012_desc-brain_mask.nii.gz
│   ├── sub-VS012_desc-preproc_T1w.nii.gz
│   ├── sub-VS012_dseg.nii.gz
│   ├── sub-VS012_from-MNI152NLin2009cAsym_to-T1w_mode-image_xfm.h5
│   ├── sub-VS012_from-orig_to-T1w_mode-image_xfm.txt
│   ├── sub-VS012_from-T1w_to-MNI152NLin2009cAsym_mode-image_xfm.h5
│   ├── sub-VS012_label-CSF_probseg.nii.gz
│   ├── sub-VS012_label-GM_probseg.nii.gz
│   ├── sub-VS012_label-WM_probseg.nii.gz
│   ├── sub-VS012_space-MNI152NLin2009cAsym_desc-brain_mask.nii.gz
│   ├── sub-VS012_space-MNI152NLin2009cAsym_desc-preproc_T1w.nii.gz
│   ├── sub-VS012_space-MNI152NLin2009cAsym_dseg.nii.gz
│   ├── sub-VS012_space-MNI152NLin2009cAsym_label-CSF_probseg.nii.gz
│   ├── sub-VS012_space-MNI152NLin2009cAsym_label-GM_probseg.nii.gz
│   └── sub-VS012_space-MNI152NLin2009cAsym_label-WM_probseg.nii.gz
├── dwi
│   ├── sub-VS012_acq-cusp66b3000_dir-ap_confounds.tsv
│   ├── sub-VS012_acq-cusp66b3000_dir-ap_desc-ImageQC_dwi.csv
│   ├── sub-VS012_acq-cusp66b3000_dir-ap_desc-SliceQC_dwi.json
│   ├── sub-VS012_acq-cusp66b3000_dir-ap_dwiqc.json
│   ├── sub-VS012_acq-cusp66b3000_dir-ap_space-T1w_desc-brain_mask.nii.gz
│   ├── sub-VS012_acq-cusp66b3000_dir-ap_space-T1w_desc-eddy_cnr.nii.gz
│   ├── sub-VS012_acq-cusp66b3000_dir-ap_space-T1w_desc-preproc_dwi.b
│   ├── sub-VS012_acq-cusp66b3000_dir-ap_space-T1w_desc-preproc_dwi.bval
│   ├── sub-VS012_acq-cusp66b3000_dir-ap_space-T1w_desc-preproc_dwi.bvec
│   ├── sub-VS012_acq-cusp66b3000_dir-ap_space-T1w_desc-preproc_dwi.nii.gz
│   └── sub-VS012_acq-cusp66b3000_dir-ap_space-T1w_dwiref.nii.gz

the sub-VS012_from-orig_to-T1w_mode-image_xfm.txt file stores an identity transform

#Insight Transform File V1.0
#Transform 0
Transform: MatrixOffsetTransformBase_double_3_3
Parameters: 1 0 0 0 1 0 0 0 1 0 0 0
FixedParameters: 0 0 0

I have segmented structures stored as multi-label nifti files that were performed in the T1 space of the original T1 file.
image

How can I transform these segmentation files into the desc-preproc_T1w space :question:

With the following files I ran :

:one: I ran

t1_mni='anat/sub-VS012_space-MNI152NLin2009cAsym_desc-preproc_T1w.nii.gz'
transform_t1_to_mni='anat/sub-VS012_from-T1w_to-MNI152NLin2009cAsym_mode-image_xfm.h5'
my_mask_in_t1='seg_in_T1.nii.gz'
my_transformed_mask='output_v1.nii.gz'

antsApplyTransforms \
--default-value 0 \
--float 0 \
--input ${my_mask_in_t1} \
--input-image-type 0 \
--interpolation MultiLabel \
--output ${my_transformed_mask} \
--reference-image ${t1_mni} \
--transform ${transform_t1_to_mni} 

but that resulted in
image

:two: I ran

t1_mni='anat/sub-VS012_space-MNI152NLin2009cAsym_desc-preproc_T1w.nii.gz'
transform_t1_to_mni='anat/sub-VS012_from-T1w_to-MNI152NLin2009cAsym_mode-image_xfm.h5'
transform_ac_pc='/work/qsiprep_wf/single_subject_VS012_wf/anat_preproc_wf/skullstrip_wf/rigid_acpc_align/transform0GenericAffine.mat'
my_mask_in_t1='seg_in_T1.nii.gz'
my_transformed_mask='output_v2.nii.gz'

antsApplyTransforms \
--default-value 0 \
--float 0 \
--input ${my_mask_in_t1} \
--input-image-type 0 \
--interpolation MultiLabel \
--output {my_transformed_mask} \
--reference-image ${t1_mni} \
--transform ${transform_t1_to_mni} 
--transform ${transform_acpc}

image

In both cases, the structures are misaligned.
Am I missing a transform somewhere ?

Thank you for your help

All the best,
Quentin

Hi,

I don’t think you need to use the MNI file at all. The only thing you should need to do is apply the forward T1-to-ACPC transform to your label file.

Best,
Steven

Hi Steven, thank you for your feedback.
It is still misaligned
image

t1_mni='anat/sub-VS012_space-MNI152NLin2009cAsym_desc-preproc_T1w.nii.gz'
transform_ac_pc='/work/qsiprep_wf/single_subject_VS012_wf/anat_preproc_wf/skullstrip_wf/rigid_acpc_align/transform0GenericAffine.mat'
my_mask_in_t1='seg_in_T1.nii.gz'
my_transformed_mask='output_v3.nii.gz'

antsApplyTransforms \
--default-value 0 \
--float 0 \
--input ${my_mask_in_t1} \
--input-image-type 0 \
--interpolation MultiLabel \
--output {my_transformed_mask} \
--reference-image ${t1_mni} \
--transform ${transform_acpc}

Why are you using the MNI file as a reference?

Sorry, this was a mistake but when I correct it :

t1='anat/sub-VS012_desc-preproc_T1w.nii.gz'
transform_ac_pc='/work/qsiprep_wf/single_subject_VS012_wf/anat_preproc_wf/skullstrip_wf/rigid_acpc_align/transform0GenericAffine.mat'
my_mask_in_t1='seg_in_T1.nii.gz'
my_transformed_mask='output_v4.nii.gz'

antsApplyTransforms \
--default-value 0 \
--float 0 \
--input ${my_mask_in_t1} \
--input-image-type 0 \
--interpolation MultiLabel \
--output ${my_transformed_mask} \
--reference-image ${t1} \
--transform ${transform_acpc}

I still get misaligned segmentation wrt T1.
image

And finally, what I want is to transform the labels to the MNI space anyway.

Quentin

Have you tested whether the original T1 file is aligned with ACPC when using that transform?

Hi Quentin,

I think you are right, it is not possible to get from the native space to the MNI space with only the transforms saved in derivatives by QSIPREP.
What you need is to save the temporary files of qsiprep ( with the -w option in your qsiprep command).

  • The _from-orig_to-T1w_mode-image_xfm.txt file is the registration between an individual T1w to the template T1w in case where multiple T1w are present. If only one T1w is present, this transform is the identity. (more info here)
    I identify three stages to get to the MNI:
  1. The native image is transformed into a _lps image using mapflow but I don’t see the command used in QSIPREP. My guess is that there must be some deobliquing and changing of the image orientation to LPS with AFNI. The directory in the temporary folder where this _lps image is stored is: qsiprep_wf/single_subject_pilote2_wf/anat_preproc_wf/anat_template_wf/t1_conform/mapflow/_t1_conform0/sub-pilote2_T1w_lps.nii.gz

  2. To find the native T1w to the acpc space transform, you have to look into in temporary folder:
    qsiprep_wf/single_subject_pilote2_wf/anat_preproc_wf/skullstrip_wf/rigid_acpc_align
    The command to calculate the transform looks like that:

antsRegistration --collapse-output-transforms 1 --dimensionality 3
 --initial-moving-transform [ /usr/local/miniconda/lib/python3.8/site-packages/qsiprep/data/mni_1mm_t1w_lps_brain.nii.gz, /work/temp_data_PREDYS/qsiprep_wf/single_subject_pilote2_wf/anat_preproc_wf/skullstrip_wf/t1_skull_strip/highres001_N4Corrected0.nii.gz, 1 ]
 --initialize-transforms-per-stage 0 --interpolation LanczosWindowedSinc 
 --output [ transform, transform_Warped.nii.gz ] --transform Rigid[ 0.2 ]
 --metric Mattes[ /usr/local/miniconda/lib/python3.8/site-packages/qsiprep/data/mni_1mm_t1w_lps_brain.nii.gz, /work/temp_data_PREDYS/qsiprep_wf/single_subject_pilote2_wf/anat_preproc_wf/skullstrip_wf/t1_skull_strip/highres001_N4Corrected0.nii.gz, 1, 32, Random, 0.25 ] 
 --convergence [ 10000x1000x10000x10000, 1e-06, 10 ] --smoothing-sigmas 7.0x3.0x1.0x0.0vox --shrink-factors 8x4x2x1 
 --use-histogram-matching 1 --winsorize-image-intensities [ 0.025, 0.975 ]  --write-composite-transform 0

The output transform is: transform0GenericAffine.mat

To apply this transform to the native T1w image, the command is then:

antsApplyTransforms --default-value 0 --float 0 
 --input /work/temp_data_PREDYS/qsiprep_wf/single_subject_pilote2_wf/anat_preproc_wf/skullstrip_wf/t1_skull_strip/highres001_BrainExtractionBrain.nii.gz
 --input-image-type 0 --interpolation LanczosWindowedSinc 
 --output highres001_BrainExtractionBrain_trans.nii.gz 
--reference-image /usr/local/miniconda/lib/python3.8/site-packages/qsiprep/data/mni_1mm_t1w_lps_brain.nii.gz
 --transform /work/temp_data_PREDYS/qsiprep_wf/single_subject_pilote2_wf/anat_preproc_wf/skullstrip_wf/rigid_acpc_align/transform0GenericAffine.mat
  1. This defines your space-T1w from which you can get to the MNI using for instance the sub-pilote2_from-T1w_to-MNI152NLin2009cAsym_mode-image_xfm.h5 transform.

In brief, in your case Quentin, you need to:

  1. bring your segmentation to the _lps orientation (it is a missing piece for me)
  2. apply the transform0GenericAffine.mat transformation to go the ACPC (= _space-T1w)
  3. apply the _from-T1w_to-MNI152NLin2009cAsym_mode-image_xfm.h5transform` to go to the MNI space.

Of course, to go from _lps to MNI, it is better to apply steps 2 and 3 in one step to get only one interpolation.

Hi Julien, thank you so much for going through these steps with me :+1:.

I did save the temporary files -w and this is why I could use the transform0GenericAffine.mat rigid transformation to go to the ACPC.

However, when digging into those intermediate files, I missed the _lps reorientation part. This is the missing transformation file in my case. It seems it is not stored.

Yes, I confirm, I don’t know either for the moment how to reproduce the transformation from the original T1w image to the _lps image which is stored here:

qsiprep_wf/single_subject_pilote2_wf/anat_preproc_wf/anat_template_wf/t1_conform/mapflow/_t1_conform0/sub-pilote2_T1w_lps.nii.gz

It seems to be a generic workflow, used in FMRIPREP as well and I would be interested to know the list of commands which are used to get to this file.

I tried a simple:

3dWarp -deoblique -prefix sub-pilote2_T1w_deoblique.nii.gz sub-pilote2_T1w.nii.gz

which gives me the sub-pilote2_T1w_deoblique.nii.gz image in LPS orientation (my original image was in RAS) but stil a different position than the sub-pilote2_T1w_lps.nii.gz produced by qsiprep.

I guess it must be linked to this process: qsiprep/images.py at 1bcb7e32db1d68d9ed0f08be43e6fa7d47c87119 · PennLINC/qsiprep · GitHub

I am trying to understand this part…

Your best option is probably to use antsRegistration to register your unprocessed T1w to the the desc-preproc_T1w in the qsiprep outputs with a Rigid transformation. The transform file can then be applied to your segmentation. This is what happens when getting the freesurfer processed T1w into alignment with the desc-preproc_T1w in the hsvs workflow, for example.

There are some header tricks that happen to images when they enter qsiprep that allow them to be read/interpreted uniformly across all the software packages. The deobliquing is one of these, which I’m not sure how to translate into an ITK transform file.

1 Like