Skip to content

[RTM] Replace FLIRT-BBR with FreeSurfer bbregister when FS preprocessing is enabled #362

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Mar 18, 2017

Conversation

effigies
Copy link
Member

Closes #64

@chrisgorgo
Copy link
Contributor

It would be important to make sure that the transformation matrix produced by bbregister moves the image to the original T1 space not to the FS conformed T1.

@effigies effigies force-pushed the bbregister branch 2 times, most recently from c736fe9 to cfbeee4 Compare February 21, 2017 14:50
@effigies
Copy link
Member Author

@oesteban Could you have a look at https://circleci.com/gh/effigies/fmriprep/102?

This node is getting mis-connected, it looks like.

170221-15:17:46,817 workflow INFO:

	 Executing: DemeanFmap ID: 36

170221-15:17:46,869 workflow INFO:

	 Executing node DemeanFmap in dir: /scratch/workflow_enumerator/100185/phase_diff_and_magnitudes/DemeanFmap

Traceback (most recent call last):

  File "/root/src/nipype/nipype/pipeline/plugins/base.py", line 249, in run

    result=result))

  File "/root/src/nipype/nipype/pipeline/plugins/base.py", line 300, in _clean_queue

    raise RuntimeError("".join(result['traceback']))

RuntimeError: Traceback (most recent call last):

  File "/root/src/nipype/nipype/pipeline/plugins/multiproc.py", line 52, in run_node

    result['result'] = node.run(updatehash=updatehash)

  File "/root/src/nipype/nipype/pipeline/engine/nodes.py", line 366, in run

    self._run_interface()

  File "/root/src/nipype/nipype/pipeline/engine/nodes.py", line 476, in _run_interface

    self._result = self._run_command(execute)

  File "/root/src/nipype/nipype/pipeline/engine/nodes.py", line 607, in _run_command

    result = self._interface.run()

  File "/root/src/nipype/nipype/interfaces/base.py", line 1085, in run

    runtime = self._run_wrapper(runtime)

  File "/root/src/nipype/nipype/interfaces/base.py", line 1033, in _run_wrapper

    runtime = self._run_interface(runtime)

  File "/root/src/nipype/nipype/interfaces/utility.py", line 498, in _run_interface

    out = function_handle(**args)

  File "<string>", line 16, in demean_image

NameError: name 'NUMPY_MMAP' is not defined

Interface Function failed to run. 

This shouldn't have been touched by the changes here. But I suppose it's possible that it's something on the nipype end with your recent PRs that we haven't adjusted for? (I'm pinning a newer nipype.)

@chrisgorgo
Copy link
Contributor

BTW - I think you can switch to main branch on both niworkflows and nipype now.

@effigies
Copy link
Member Author

I have a niworkflows branch (no PR yet) with changes needed for this bit. When I'm sure I don't need more, I'll put a PR there.

contrast_type='t2',
init='header',
registered_file=True,
out_fsl_file=True,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Running BBRegisterRPT, I'm running into the issue that registered_file and out_reg_file are undefined in the outputs, but not in the command (which uses _list_outputs to build the command). Neither exists in the working directory. Weirdly, out_fsl_file is defined and exists, so it doesn't seem like it should be an mixin/MRO issue.

Node: sub-387 (ref_epi_t1_registration (bbregister (registration)
=================================================================

 Hierarchy : workflow_enumerator.sub-387.ref_epi_t1_registration.bbregister
 Exec ID : bbregister.a2

Original Inputs
---------------

* args : <undefined>
* compress_report : auto
* contrast_type : t2
* environ : {'SUBJECTS_DIR': '/opt/freesurfer/subjects'}
* epi_mask : <undefined>
* generate_report : True
* ignore_exception : False
* init : header
* init_reg_file : <undefined>
* intermediate_file : <undefined>
* out_fsl_file : True
* out_reg_file : <undefined>
* out_report : report.svg
* reg_frame : <undefined>
* reg_middle_frame : <undefined>
* registered_file : True
* source_file : /scratch/workflow_enumerator/sub-387/ref_epi_t1_registration/_epi_..data..sub-387..func..sub-387_task-Beast_run-3_bold.nii.gz/explicit_mask_epi/sub-387_task-Beast_run-3_bold_mcf.nii.gz_mean_reg_masked.nii.gz
* spm_nifti : <undefined>
* subject_id : sub-387
* subjects_dir : /out/freesurfer
* terminal_output : allatonce

Execution Inputs
----------------

* args : <undefined>
* compress_report : auto
* contrast_type : t2
* environ : {'SUBJECTS_DIR': '/out/freesurfer'}
* epi_mask : <undefined>
* generate_report : True
* ignore_exception : False
* init : header
* init_reg_file : <undefined>
* intermediate_file : <undefined>
* out_fsl_file : True
* out_reg_file : <undefined>
* out_report : report.svg
* reg_frame : <undefined>
* reg_middle_frame : <undefined>
* registered_file : True
* source_file : /scratch/workflow_enumerator/sub-387/ref_epi_t1_registration/_epi_..data..sub-387..func..sub-387_task-Beast_run-3_bold.nii.gz/bbregister/sub-387_task-Beast_run-3_bold_mcf.nii.gz_mean_reg_masked.nii.gz
* spm_nifti : <undefined>
* subject_id : sub-387
* subjects_dir : /out/freesurfer
* terminal_output : allatonce

Execution Outputs
-----------------

* min_cost_file : <undefined>
* out_fsl_file : /scratch/workflow_enumerator/sub-387/ref_epi_t1_registration/_epi_..data..sub-387..func..sub-387_task-Beast_run-3_bold.nii.gz/bbregister/sub-387_task-Beast_run-3_bold_mcf.nii.gz_mean_reg_masked_bbreg_sub-387.mat
* out_reg_file : <undefined>
* out_report : /scratch/workflow_enumerator/sub-387/ref_epi_t1_registration/_epi_..data..sub-387..func..sub-387_task-Beast_run-3_bold.nii.gz/bbregister/report.svg
* registered_file : <undefined>

Runtime info
------------

* command : bbregister --t2 --init-header --fslmat /scratch/workflow_enumerator/sub-387/ref_epi_t1_registration/_epi_..data..sub-387..func..sub-387_task-Beast_run-3_bold.nii.gz/bbregister/sub-387_task-Beast_run-3_bold_mcf.nii.gz_mean_reg_masked_bbreg_sub-387.mat --reg /scratch/workflow_enumerator/sub-387/ref_epi_t1_registration/_epi_..data..sub-387..func..sub-387_task-Beast_run-3_bold.nii.gz/bbregister/sub-387_task-Beast_run-3_bold_mcf.nii.gz_mean_reg_masked_bbreg_sub-387.dat --o /scratch/workflow_enumerator/sub-387/ref_epi_t1_registration/_epi_..data..sub-387..func..sub-387_task-Beast_run-3_bold.nii.gz/bbregister/sub-387_task-Beast_run-3_bold_mcf.nii.gz_mean_reg_masked_bbreg.nii.gz --mov /scratch/workflow_enumerator/sub-387/ref_epi_t1_registration/_epi_..data..sub-387..func..sub-387_task-Beast_run-3_bold.nii.gz/bbregister/sub-387_task-Beast_run-3_bold_mcf.nii.gz_mean_reg_masked.nii.gz --s sub-387
* duration : 67.60915
* hostname : 9c7c0a3e6622

Terminal output
~~~~~~~~~~~~~~~

@chrisfilo Anything obvious stick out to you?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh. source_file changed.

Was: [...]/_epi_..data..sub-387..func..sub-387_task-Beast_run-3_bold.nii.gz/explicit_mask_epi/sub-387_task-Beast_run-3_bold_mcf.nii.gz_mean_reg_masked.nii.gz
Now: [...]/_epi_..data..sub-387..func..sub-387_task-Beast_run-3_bold.nii.gz/bbregister/sub-387_task-Beast_run-3_bold_mcf.nii.gz_mean_reg_masked.nii.gz

Looks like a nipype issue. I'll try to figure this out.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh. remove_unnecessary_outputs is True by default.

It does seem like if I say "Make this file", it should at least throw a warning if it's unused, but I suppose setting a config variable is easy enough.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should definitely make reportlets work independently off what remove_unnecessary_outputs is set to.

As far as I remember with DEBUG logging you get information which files are removed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reportlets work fine (mainly because we hook them up to an output sink). It's the outputs like the registered file (which I wanted to visually compare to the FLIRT-registered file) that get dropped if there isn't something else grabbing them.

Unfortunately, needed_outputs is an attribute of Node, not BaseInterface, so there's no simple way for an interface to mark the outputs that it uses as do-not-erase.

And yeah, the DEBUG logging is how I realized they were at least being created.

@chrisgorgo
Copy link
Contributor

Probably has something to do with copyfile.

@effigies
Copy link
Member Author

Yeah, it did, and wasn't at all the problem. Well, I guess I understand more of nipype's internals now. 🥇

@effigies
Copy link
Member Author

effigies commented Feb 23, 2017

Okay. We're definitely getting different outputs, and it appears that bbregister is registering to the 256x256x256 FreeSurfer space. Some basic details for the anatomicals, functionals and realigned volumes follow. If you have clear thoughts on what should be happening that isn't, I'm all ears. I don't see options in bbregister to use a different target space, but I'll try to dig a little deeper. (Sometimes they have undocumented options.)

Visually, the output images align.

sub-387_T1w.nii.gz

Dimensions: 256 x 256 x 186
Voxel sizes: 0.8984 x 0.8984 x 0.9
Affine: [[   0.            0.           -0.89999998   78.08100128]
         [   0.           -0.89840001    0.          131.02000427]
         [  -0.89840001    0.            0.          110.31900024]
         [   0.            0.            0.            1.        ]]

sub-387_task-Beast_run-1_bold.nii.gz

Dimensions: 100 x 100 x 64 x 650
Voxel sizes: 2.2 x 2.2 x 2.2 x 0.68
Affine: [[ -2.20000005e+00   0.00000000e+00   1.99987669e-03   1.00387001e+02]
         [  7.35627196e-04  -1.97774065e+00   9.63940501e-01   8.25609970e+01]
         [ -1.50989520e-03  -9.63606596e-01  -1.97757792e+00   1.03964500e+02]
         [  0.00000000e+00   0.00000000e+00   0.00000000e+00   1.00000000e+00]]

sub-387_task-Beast_run-1_bold_mcf.nii.gz_mean_reg_masked_flirt.nii.gz

Dimensions: 186 x 256 x 256
Voxel sizes: 0.9 x 0.8984 x 0.8984
Affine: [[   0.89999998    0.            0.          -88.41899109]
         [   0.            0.89840001    0.          -98.0719986 ]
         [   0.            0.            0.89840001 -118.77300262]
         [   0.            0.            0.            1.        ]]

sub-387_task-Beast_run-1_bold_mcf.nii.gz_mean_reg_masked_bbreg.nii.gz

Dimensions: 256 x 256 x 256
Voxel sizes: 0.8984 x 0.8984 x 0.8984
Affine: [[  -0.89840013    0.            0.          109.3762207 ]
         [   0.            0.            0.89840001  -98.97039795]
         [   0.           -0.89840001    0.          110.31900024]
         [   0.            0.            0.            1.        ]]

sub-387_task-Beast_run-1_bold_mcf.nii.gz_mean_reg_masked_flirt.mat

Registration: [[  9.99822613e-01   2.18406143e-03  -1.87130549e-02  -2.01251828e+01]
               [  1.02471905e-02  -8.96531511e-01   4.42861559e-01   1.78893223e+02]
               [ -1.58096043e-02  -4.42974773e-01  -8.96394706e-01   2.26519183e+02]
               [  0.00000000e+00   0.00000000e+00   0.00000000e+00   1.00000000e+00]]

sub-387_task-Beast_run-1_bold_mcf.nii.gz_mean_reg_masked_bbreg_sub-387.mat

Registration: [[  9.99783580e-01   4.73640000e-04  -2.07878600e-02   1.14698811e+01]
               [  1.83094900e-02   4.41616650e-01   8.96942620e-01   2.37868500e+00]
               [  9.54438000e-03  -8.97202850e-01   4.41665860e-01   1.80107742e+02]
               [  0.00000000e+00   0.00000000e+00   0.00000000e+00   1.00000012e+00]]

@chrisgorgo
Copy link
Contributor

chrisgorgo commented Feb 23, 2017 via email

@effigies effigies force-pushed the bbregister branch 3 times, most recently from ecb18f0 to 584367e Compare February 24, 2017 22:54
@effigies
Copy link
Member Author

So 7e56d04 implements the FreeSurfer-conformed volume -> original T1w volume transformation. However, now that I'm trying to think through it, I'm confusing myself.

Basic idea: t1mgz_affine = nb.load('T1.mgz').affine :: ijk -> LIA (in FreeSurfer-conformed volume), and t1w_affine =nb.load('t1w.nii.gz').affine :: ijk -> IPL (for example; in raw MGZ space). Hence, pinv(t1mgz_affine).dot(t1w_affine) :: LIA -> IPL... except why should the ijks correspond?

@satra, if you get a second, could you have a look and let me know if this is what you were describing? If anybody else sees something obvious, let me know.

@satra
Copy link

satra commented Feb 25, 2017

fsl bbr -> fsl1.mat
fs bbr -> fsl2.mat 

fsl1.mat -> reference is regular T1.nii
fsl2.mat -> reference is freesurfer space (likely same as T1.mgz, but i'm never 100% sure till i try it out)

so technically if you know the mapping between T1.nii and freesurfer space, you should be able to go one way or the other. of the top of my head i was thinking that this would be the same as the mapping between fsl1.mat and fsl2.mat i.e., pinv(fsl1.mat).dot(fsl2.mat) or vice verse depending on which transform you want.

t1w_affine maps from ijk_t1w to xyz
t1mgz_affine maps from ijk_mgz to xyz

it's the xyz that's common to both, which is what allows you to relate them.

@effigies
Copy link
Member Author

I think what you describe -- mapping between the two structural spaces using the functional registrations -- assumes that FSL BBR and FS bbregister are going to produce the same alignment? If that were the case, we should just choose the one that puts us in the space we want, in this case FSL.

I'd rather avoid the functional step altogether and use the structural affines, and given that FreeSurfer is doing nothing but resampling to 256^3 isometric voxels, this shouldn't be fancy. If what we're after is ijk_mgz -> ijk_t1w, I think we should be able to just turn this around and say t1mgz_affine.dot(pinv(t1w_affine)).

I might still be confused, but I feel like this make more sense.

@satra
Copy link

satra commented Feb 25, 2017

@effigies - i wasn't saying you should map between functional spaces - since you don't really want to run both registrations. just that the mapping between those two spaces should roughly equal the structural transform as a sanity check.

however, each registration has it's own source of error so simply doing a structural transform is not going to be equivalent to running the other registration.

the structural transform is still the safest thing to do.

@effigies effigies force-pushed the bbregister branch 7 times, most recently from 6617b2a to 8f7cb5d Compare March 6, 2017 16:02
@effigies
Copy link
Member Author

effigies commented Mar 8, 2017

Wondering if this is causing an issue: Image+centering.pdf

The location of the center defined by the affine in the raw images seems to be very different, and the center in the flt_bbr'ed a bbregistered images are also different. Is this normal/something that shouldn't affect the affine correction?

@effigies
Copy link
Member Author

So this has gone on for quite a while, and I want to try to give a status update, to clear my head and get people's opinions on how best to proceed. I'm testing on ds000005 (give or take some 0's) to avoid LR flip issues in the uh2 datasets.

We have been using the following FLIRT sequence (actual nipype filenames simplified):

$ flirt -in $EPI -ref $T1 -out init.nii.gz -omat init.mat -dof 6
$ flirt -in $EPI -ref $T1 -omat flirt.mat -searchcost bbr -dof 6 -init init.mat \
    -schedule /usr/share/fsl/5.0/etc/flirtsch/bbr.sch -wmseg $WMMASK

Here T1 refers to the ANTs-generated brainmask, not the original T1 in the BIDS folder.

Yielding flirt.mat:

 0.9997169368   -0.008484848857  -0.02222782656  -15.35086442
-0.006684317239  0.7964569286    -0.6046583701    70.34123553 
 0.02283393991   0.6046357384     0.7961747509    15.49617409
 0  0  0  1

We're replacing this with:

$ recon-all -s $SUBJ -i $ORIG_T1 -all
$ bbregister --s $SUBJ --mov $EPI --init-coreg --t2 --reg bbreg.dat --fslmat fsl.mat
$ tkregister2 --no-edit --mov $EPI --reg bbreg.dat --targ $T1 --fslregout adjusted.mat

This yields fsl.mat (output of bbregister):

 0.99972117   -0.01106763   -0.02086867   33.85606003
-0.02328384   -0.61062598   -0.79157710  240.28797913
-0.00398207    0.79184198   -0.61071330   70.66278839
 0.00000000    0.00000000    0.00000000    1.00000000

And adjusted.mat (output of tkregister2):

-0.99972129    0.01106763    0.02086867  173.14398193
-0.02328384   -0.61062604   -0.79157716  240.28800964
-0.00398206    0.79184198   -0.61071336   70.66275787
-0.00000000   -0.00000000   -0.00000000    1.00000012 

These aren't the same as flirt.mat, but then, the affine matrices of the outputs of flirt and bbregister are, respectively:

   1.            0.            0.          -81.
   0.            1.33333302    0.         -133.
   0.            0.            1.33333302 -129.
   0.            0.            0.            1.
  -0.99999994    0.            0.          127.00000763
   0.            0.            0.99999994 -133.
   0.           -0.99999994    0.          126.99995422
   0.            0.            0.            1.

Aligning these on Freeview shows basically identical images:

flirt

bbr

So it may be that I've been making too much of differing afffines and transforms, and this is a perfectly acceptable place to be; the aligned functionals are scaled and centered slightly differently, but we have transforms back into the original T1 space. Maybe the best way to test is to determine whether or not the downstream workflow is unable to cope with these differences. What's most likely to get distorted by a messed up

Alternately, what's the best way to directly test the transforms? flirt -in bbregistered.nii.gz -ref $T1 -init adjusted.mat -applyxfm -o out.nii.gz produces those weirdly flipped images:

applyxfm

@chrisfilo @oesteban

@effigies effigies force-pushed the bbregister branch 2 times, most recently from 000e193 to 10b05b2 Compare March 15, 2017 19:28
Copy link
Contributor

@chrisgorgo chrisgorgo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code looks good, but I'm going to try to run it to test things out. One thing that would be good to add are changes to documentation - explaining which coregistration is used depending on different flags.

@effigies
Copy link
Member Author

effigies commented Mar 16, 2017

Docs updated. I decided to go with the approach of noting when FreeSurfer adds to or modifies a step in the base workflow, and then describing all surface-based processing in its own section. I suspect that will be smoother when fieldmaps go in, and it allows the FreeSurfer derivatives to be described without cluttering up the main derivatives section.

An alternative approach would be to include FreeSurfer stuff by default, and have a section describing the removals/replacements if --no-freesurfer is used.

@chrisgorgo
Copy link
Contributor

there seems to be something wrong with the MNI preproc files:

this is the preproc bold in the MNI space overlaid on top of the atlas
image

this is the proproc bold in the T1w space overlaid on top of the T1
image

This is the normalized T1w overlaid on the MNI
image

ds005 sub-01

I'm gonna check if the same thing happens in master.

@chrisgorgo
Copy link
Contributor

The same thing when using master with FLIRT BBR

image

@chrisgorgo chrisgorgo merged commit 9f15a0f into nipreps:master Mar 18, 2017
@effigies effigies deleted the bbregister branch March 18, 2017 00:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants