-
Notifications
You must be signed in to change notification settings - Fork 264
WIP: fix and refactor par / rec read / write #227
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
Conversation
Looks good. Where does the shift in the xform come from. Is that relative to the FoV origin, or scanner center? |
Actually - they seem to generate the same affine ('--origin=fov', '--origin=scanner')... |
Michael - any comments on the affine thing? Would you mind checking that? |
This does not address the issue mentioned in nipygh-227 that Philips own NIfTI conversion yields a different offset column in the affine.
I took a first look. For me --origin 'fov' yields a different affine than 'scanner'. I added a test for that. I did not figure out why the offset in the Philips conversion differs. I'll try some other converters to get a second opinion before I start surgery. |
Part of the problem in nipygh-227
So I think I fixed the biggest issue (pushed to my fork). Here is the default ("scanner") affine that is produced now:
This is pretty close to the Philips interpretation:
It seems like for the first two offset values they shift by a half-voxel (we have 3.75x3.75 in-plane resolution) in this file. But I have no clue where the missing 0.43 for the last axis should come from. Any ideas? |
Hmm, interesting: the acquisition console screenshot for the data says slice thickness 6mm and slice gap 1mm. But exported PAR/REC, XML/REC and NII all claim 2mm gap or 8mm slice thickness. not sure who is at fault here... screenshot here: http://psydata.ovgu.de/philips_achieva_testfiles/conversion/console_screenshot.png |
parrec uses iteration over lines of a text file; implement this in the Openers wrapper and test.
Thanks Michael H for releasing it.
Test against values from converted NIfTI header
Adapt to BinOpener fileobj object as input. Change returned TR value to seconds instead of milliseconds. Add get_data_offset and get_slope_inter methods for compatibility with standard ArrayProxy object. Add option to set default scaling as input to header __init__
Use new PAR/REC data and Philips converted nifti image to test against.
Change 'transversal' to 'transverse'; 'sagital' to 'sagittal'. I think these are the standard names: http://en.wikipedia.org/wiki/Sagittal_plane
numpy strings expected to be bytes, not strings. Files must get opened with binary flag.
Make bytes / strings explicit. gzip doesn't appear to allow opening for read in text mode in Python 3.
This does not address the issue mentioned in nipygh-227 that Philips own NIfTI conversion yields a different offset column in the affine.
Part of the problem in nipygh-227
Newlines between methods; docstring formatting and adding docstring return value descriptions; expanded description of format.
read_img_data trying to set offset of 0 even when offset is already set to 0, therefore breaking for headers that can't do ``set_data_offset``. Check whether value is already 0, don't set if so.
par header format enforces data offset of 0. Implement set_data_offset for par header enforcing this.
Replace with recommended ``dataobj.get_unscaled`` method.
The PAR file obviously does think there's an 6mm slice thickness with a 2mm
There are 9 slices, and the FOV is:
That fits, no? (6 * 9 + 2 * 8)? By the way, I notice that I played around with the affine but didn't get anywhere. I refactored a bit, |
Sorry - I have terrible internet here in Cuba - what does the XML file say as to the slice gap? |
Test we're getting the same affine as we were (it may be the wrong affine, but at least it is our affine). Test we're getting the same-ish 3x3 component as the Philips generated NIfTI image.
A few typos and thinkos
* Rephrase the transformations in terms of LPS instead of RAS in the hope of making the code easier to read * Remove safety-net for the rotations, I'm confident the r2agui approach and euler2mat do the same thing. For clarity, unpack the order of rotations with euler2mat in case we need to fix the order later. * Use voxel FOV to get FOV offset in voxel space, then transform to get the FOV offset in mm It's possible this refactoring breaks or fixes handling of non-transverse images - we really need some of those to test against.
Hopefully, yeah. I might be able to open a PR into your PR. Here are the cases I think we need to detect:
One thing to correct:
@mrjeffs is that a brief summary of the issues we've hit thus far? |
Sorry, I meant "open a PR into your branch" not "into your PR"... |
:) - mi casa tu casa. We should certainly fix orientation of sagittal images - I hope Michael's tests will help out with that. |
Here are the simple test scans: http://psydata.ovgu.de/philips_achieva_testfiles/ the new ones are in the conversion2/ folder. The README in the top-folder should have the necessary bits of information. |
@matthew-brett are you planning to look at those sample files and check out the saggital orientation stuff sometime this week? In the meantime I'm working with @mrjeffs to get representative sample files from our system that satisfy the problematic conditions I outlined above. I'm going to try to look into it in the next couple days to make some progress by the end of the week. |
I am planning to get to this tomorrow - yes - so if you can get something done in the next few days, that would be a great help. |
Totally forgot about Michael's collection of phantom data and yesterday collected some on a local Philips box, aiming to come up with quite obscure setup (tilt, resolution different in all dimension, small matrix size, slice gaps) covering major acquisition protocols (T1/T2/T2*/DTI/fieldmap) and also exporting in DICOMs (as a single file per image), PAR/REC, NIFTI (non FSL one). Overall size of the .git for such a collection is just 1.6MB, while I think it could cover quite a wide range of test cases -- I would appreciate if you have a look: https://github.com/yarikoptic/nitest-balls1 , may be they would come of some use? ;) |
Just, quickly: I had a brief look at the affines produced by the current state of the branch for the scans with cor,sag,tra slice orientation and no further rotation or translation. All I know so far is that only axial/transverse orientiation assigns proper axis labels in FSLView. Each image should have the marker (more or less) clearly visible with min/max settings of 1000/20000. One marker for anterior, two for inferior and three for left. That is all I got... |
Sorry to be slow on this one, I will review these in the next couple of days and get back to y'all. |
Cool. I assume you haven't had time to work on the transformation issues, then...? :) |
Sorry - not yet. |
It almost seems like you have more responsibilities than working on |
Function for chaining dot product across arrays, particularly useful for dot product of affine components
Michael's new data had enough variation to test most combinations of transformations. The key seems to be to transform first to the Philips axes (anterior-posterior, inferior-superior, right-left), then apply the rotations, before finally taking into RAS output space. Add the PAR files for Michael's test data set and test against the current versions of the affines, which I checked by reslicing the data with the affines (see check_parrec_reslice.py script).
Michael's new dataset was very useful in sorting this out. The affine transforms for Michael's conversion2 dataset appear to be about The FSL nifti affines for conversion2 seemed to be a bit of a mess, so I was We're still not dealing well with sequences with interleaved slices and so on. Probably the way to fix that would be to make a new ImageArrayProxy object But I believe the orientation is right now, although I can't test the effect I think this current PR is ready to merge, do y'all agree? Then rebase for Now I know the format a bit better, I have a feeling we could get the sorting |
Eventually an enhanced I don't see a commit here -- did the rotations work already with your current changeset, or is the commit pending? I could check to see whether our data are oriented properly now. |
Sorry - just sending commits now... |
Shoot, it created merge conflicts with my PR. We have a few options:
Let me know which you prefer... |
Oh, by the volume/slice sorting I guess you mean my PR... yeah I'm fine with you rebasing this, merging it, and the I'll rebase and open a PR into |
OK - sounds reasonable. I'll have a look at your PR today or Would you mind having a look at the latest commits for a review? With |
Edits look reasonable to me (although I am certainly no transformation expert), but perhaps more importantly the changes have fixed the orientations in our data! Awesome. |
OK - thanks - then I will merge - I think this is an improvement. Can |
Done. |
Done: #253. |
This does not address the issue mentioned in nipygh-227 that Philips own NIfTI conversion yields a different offset column in the affine.
Part of the problem in nipygh-227
par / rec read write wasn't tested and I'd broken it some time ago by
some refactoring of file IO and of the ArrayProxy class.
I fixed my breakages and also:
ArrayProxy class
I've tested against the nifti file that Philips generated from the same
data -- from : http://psydata.ovgu.de/philips_achieva_testfiles/
A remaining issue is that our affine differs from the affine Philips
writes into its own conversion to Nifti::