-
Notifications
You must be signed in to change notification settings - Fork 24
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
[ENH] Vector-checking utilities #26
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be great to have tests here!
@arokem -- basic tests are now included in dmriprep/utils/tests |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this contribution. I'm missing a few things:
- A Nipype interface that can be embedded in the workflow.
- More test cases of bvec/bval examples which include intentional bad cases and edge cases (e.g. bvecs with norm very close to 1 and very close to 0, but none of those).
- More clear delineation of responsibilities: more functions with atomic functionality. As an exception, I believe it could be much more readable and robust to do all the rescalings (bvec and bval) in just one function.
BTW - happy to help as much as I can sending you PRs to your branch! |
@oesteban and @arokem -- Remaining to-do's: |
976a317
to
4e0233f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking much much better, please keep up with the good work!
Glad to hear it. |
one question to @arokem, @garikoitz, et al.: I guess here we are also missing the conversion between image indices coordinates and RAS coordinates for the bvecs, is that correct? I believe FSL and MRTrix use image coordinates - what is the case for dipy? |
I think FSL is image coordinates but mrtrix is scanner (real) coordinates
Wasn’t this part of Derek’s PR?
…On Sun, 27 Oct 2019 at 16:00, Oscar Esteban ***@***.***> wrote:
one question to @arokem <https://github.com/arokem>, @garikoitz
<https://github.com/garikoitz>, et al.:
I guess here we are also missing the conversion between image indices
coordinates and RAS coordinates for the bvecs, is that correct?
I believe FSL and MRTrix use image coordinates - what is the case for dipy?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#26?email_source=notifications&email_token=ABCZAV2XEGFCVESD3HPV6XDQQYMSHA5CNFSM4JBQDZL2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOECLKN6I#issuecomment-546744057>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABCZAV3USWDXOX75U566AA3QQYMSHANCNFSM4JBQDZLQ>
.
|
Well, I mean in my original PR, I had included the following:
Basically, if the dwi image is in RAS, then we leave the bvec alone. If the image is in any other orientation, we reorient it to RAS and invert the corresponding axes of the bvecs that require flipping to get there. I was under the impression that pretty much all tools use image index coordinates as the convention? Is that not the case? If so, and if we do plan to impose RAS+ orientation in dmriprep in general, it should definitely happen near the beginning of the workflow, and the bvec should be corrected accordingly... |
I took a similar approach for reorienting images and bvecs (https://github.com/PennBBL/qsiprep/blob/master/qsiprep/interfaces/images.py#L358). If you're planning on using ants for most of the registration, then keeping things in LPS+ makes life a lot easier. Internally their world coordinate system is oriented this way and it makes operations like local rotations much easier. The downside is that if you're using smriprep there will be a lot of internal image axis flipping. |
04e97e4
to
ae3a0f4
Compare
@mattcieslak -- see if you like the updated version above. It now permits any of RAS/LAS/LPS as user-specified output orientations, rather than hard-coding it to any particular one. I do see what you mean about LPS being more ANTs friendly. Any ideas why the ANTs people haven't accepted RAS+ as a standard? As you mentioned, it makes things a bit trickier for us (plus maybe also risks confusion on the part of the user?) if we go with LPS and then have to keep flipping everything back... |
@oesteban -- I've made the requested changes plus some further enhancements:
|
Why the DWIs must be in RAS+ coords? (poking @mattcieslak for this one, as he has more experience) I think how you access the data array is independent of the coordinates of the bvecs. If you have an interface that provides you both RASB and voxel coords (after all your checks), then nothing prevents you from grabbing the output you really need down the line.
I would try to avoid reorienting images by all means. If we still want to do it (and believe me, I cry when I find a NIfTI with an orientation other than RAS+), then that should be COMPLETELY independent of this PR. To calculate RASB you just need the affine of the image.
ITK/ANTs have a problem with NIfTIs define their orientation with the s-form matrix. In fMRIPrep we run a series of checks on the NIfTI headers, and make sure they all have q-form and s-form matrices that are consistent. Then you just need to be careful after any ITK-based processing as s-forms will not be updated (and thus, downstream steps will likely get orientation wrong). This is potentially going to get much much better when we finish https://github.com/poldracklab/nitransforms (which btw is coming along pretty fast).
I'll have a look ASAP
Great! I'll give this all my love. I hope there are some nice tests there :D. Please grab from nitransfroms whatever you need, it's going to become a dependency anyways (better said, it will be part of nibabel).
Sounds good. I'll probably have to send a PR to your branch with stuff related to the above two points, so I'll have this in mind and try to help.
👍
I think this is awesome. If you want to split this to a subsequent PR, that will simplify your life to get this one in, simplify mine when reviewing and also reduce the risk we get out of focus in the PR (lengthening the review period).
Don't miss your sleep on this one. Let's test the waters first (WDYT, @effigies?)
That's already there, and it's called pyBIDS :D. |
There are 83 comments in this thread. What's the specific proposal? I can make a couple general comments, and if they don't fit what you're talking about, you can provide more context. A pretty good model in the past has been to develop the IO routines you need in your own project, and once they're solidified, to add them to nibabel. For example, PySurfer did this with Also, in case it's relevant, the data types have fairly heterogeneous APIs in nibabel. Images have one API, streamlines another, GIFTIs are barely related to NIfTIs, and the FreeSurfer IO functions work with dictionaries and numpy arrays, eschewing custom Python objects altogether. So don't feel obligated to contort yourself to mimic any of these APIs, if they're not natural. Figure out what makes sense for your data. (This also argues for developing locally and upstreaming later, as you can work through a few APIs if needed.) |
Sorry it was overwhelming - you clearly found your way to the proposal (i/o and storage of bvecs and bvals for DWI).
Thanks, they do fit :)
Great, that confirms our approach. We will first develop something that works for us trying the code to be as nibabel-ish as possible but without that becoming a stopper. Then we can see if there's interest in adding them into nibabel. |
I have revised the utilities and the nipype interface, trying to simplify the code (without missing functionality). Some assumptions that can be made on inputs: * RASB tables will be BIDS-valid, and hence normalized, with absolute b-vals, etc. * bvec+bval will be in FSL format (as mandated by BIDS), and hence in image coordinates. In general, I've tried to remove repetition of code sections, added doctests that will serve for documentation, minimized dependencies, checked code style. I haven't gone through the tests, but they will need a deep revision. I would recommend using pytest fixtures to minimize the lines of code and automate some clerical tasks (e.g., setting up data, changing to a temporal folder, etc.).
Codecov Report
@@ Coverage Diff @@
## master #26 +/- ##
===========================================
+ Coverage 40.57% 54.66% +14.08%
===========================================
Files 9 11 +2
Lines 589 772 +183
Branches 92 116 +24
===========================================
+ Hits 239 422 +183
Misses 349 349
Partials 1 1
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is ready to go on my side. Some highlights:
- Sherbrooke 3-shell dataset is now pulled by pytest for the whole session and the cache directory can be changed.
- Added a few fixtures to access resources easily from both doctests and tests.
- Encapsulated b-matrix operations in one class, happy to keep working on this.
- Pretty high coverage with tests (see https://codecov.io/gh/nipreps/dmriprep/pull/26)
- Hopefully sufficient documentation with docstrings.
I believe next is:
- In-depth revision from @dPys, with two goals: i) identifying I did not leave anything out from his original implementation; ii) asking me everything that remains unclear about why I made particular changes.
- A second pass from @arokem with dipy's eyes (although I've minimized this dependency, I think it wasn't necessary for most of the implementation).
- Everyone else is invited to have a look. Perhaps @josephmje and @mattcieslak could check on this with an eye on their currently open PRs.
Just a question from my end. Is the idea to use the output rasb tsv file instead of the input bvec and bval files for all downstream operations? |
@josephmje -- I think as much as possible, yes. It is simply a more concise way to storing the vector information so that: 1) we are not restricted to relying on two separate files; and 2) we always know the orientation of the vectors relative to RAS (information that is absent from traditional bvecs alone). Now, should we choose to include FSL-based tools like EDDY (at least until dmriprep develops better ones), we would still need to augment interfacing to handle rasbtsv since those tools fundamentally rely on the bvec/bval formats -- but this shouldn't be too big a deal. |
As it is implemented right now, you will have access to both formats at all times (RAS+B and FSL-style), so you just connect the right input to the interface. |
'dwi_file': dipy_datadir / "HARDI193.nii.gz", | ||
'bvecs': np.loadtxt(dipy_datadir / "HARDI193.bvec").T, | ||
'bvals': np.loadtxt(dipy_datadir / "HARDI193.bval"), | ||
} | ||
|
||
|
||
@pytest.fixture(autouse=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is awesome.
dmriprep/utils/vectors.py
Outdated
np.savetxt(str(path), self.gradients, | ||
delimiter='\t', header='\t'.join('RASB'), | ||
fmt=['%.8f'] * 3 + ['%g']) | ||
if bvecs: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to include a conditional here or does the shape get enforced by the interface? hard to tell
if bvecs.shape[-1] == 3:
np.savetxt(str(bvecs), self.bvecs.T, fmt='%.6f')
else:
np.savetxt(str(bvecs), self.bvecs, fmt='%.6f')
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At this point, self.bvecs
has (Nx3) dimensions. Please note that bvecs
here just contains a string or a Path (perhaps improving the docstring here would be nice).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I see that now. I wonder if to avoid confusion, we should rename bvecs to fbvecs for every place we're referring to a file string/Path, and go with bvecs any time we're referring to an array?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function is called to_filename
, I don't think changing the name of the arguments will change much. I guess it is more of an interface problem, i.e., should we have to_filename
store only RASB and then add two more methods like bvecs_to_filename
and bvals_to_filename
or a write_bvec_bval(path)
that adds .bval
and .bvec
.
Another alternative is:
to_filename(filename, filetype='rasb')
and then switch to bvec/bval mode if filetype is 'fsl'
I would probably like this last one better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough. Well I like the latter alternative best, but yes I think we need something like a write_bvec_bval(path) here. I sort of had something like this previously in the interface with:
if self.inputs.save_fsl_style is True:
fbval_out_file, fbvec_out_file = vt.save_vecs_fsl()
but the to_filename would be best
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implemented the to_filename(filename, filetype='fsl')
route. 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only one point noted. All else looks amazing. Nice teamwork!
Co-Authored-By: Michael Joseph <[email protected]>
Okay, i don't think it'd be premature to merge. Let's do this! |
@dPys, please send a PR adding your name to the zenodo file. Please remember to include |
Per Issue #24
FR: Conformity check of bvecs and bvals (pure-python implementation)
Todo:
Comments/Suggestions welcome.
@dPys