dipy versus mrtrix CSD results #2959
-
I am having trouble getting CSD to give reasonable results in dipy. Any thoughts would be much appreciated! I am just using # (load dwi, WM mask, gradient table, response function)
csd_model = ConstrainedSphericalDeconvModel(gtab, response_mrtrix, sh_order=8)
csd_fit = csd_model.fit(data, mask=mask_data)
csd_shm_coeff = csd_fit.shm_coeff
save_nifti('output.nii.gz', csd_shm_coeff, affine, img.header) # (where affine and img.header are taken from the original dwi) When I use mrtrix's mrview tool and zoom in on the corpus callosum I see these funny shaped and misalgined FODs that have been generated by dipy If I use mrtrix for CSD rather than dipy, then the image looks much more plausible: Both images were made from the same region of the same underlying DWIs and using the same response function. Any thoughts what could be wrong with my approach? EDIT: I tried also the more dipy native method of visualization: from dipy.data import default_sphere
from dipy.viz import window, actor
from dipy.sims.voxel import single_tensor_odf
csd_odf = csd_fit.odf(default_sphere)
fodf_spheres = actor.odf_slicer(csd_odf, sphere=default_sphere, scale=0.9,
norm=False, colormap='plasma')
scene = window.Scene()
scene.add(fodf_spheres)
window.snapshot(scene, fname="scene.png", size=(3000, 3000)) Below we see the same slice, the same FOD image, and roughly the same region. DIPY visual on the left and mrtrix's mrview on the right: |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 11 replies
-
Hi @ebrahimebrahim, Multiple possible reasons that comes in my mind:
Let us know when you check those point. |
Beta Was this translation helpful? Give feedback.
-
Thanks for all the debugging! You know what would be really useful? A
script that converts between the two software libraries basis sets. Would
you like to contribute that to DIPY? It seems like you know all the ins and
outs of this issue at this point, so you'd be really well-prepared to do
that.
…On Sun, Nov 5, 2023 at 9:53 AM Ebrahim Ebrahim ***@***.***> wrote:
After another round of debugging, I finally caught why the two bases are
*not* the same.
The difference is in the m>0 here
<https://github.com/dipy/dipy/blob/618b39525ad5276f774b9920da983920d59cf340/dipy/reconst/shm.py#L357>
.
If it were m<0 then it would exactly match the mrtrix basis
<https://mrtrix.readthedocs.io/en/latest/concepts/spherical_harmonics.html#formulation-used-in-mrtrix3>
.
—
Reply to this email directly, view it on GitHub
<#2959 (reply in thread)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAA46NUOP5WDIIXGV3AWNOTYC7AB7AVCNFSM6AAAAAA6U7OHHCVHI2DSMVQWIX3LMV43SRDJONRXK43TNFXW4Q3PNVWWK3TUHM3TIOBQGYZTK>
.
You are receiving this because you are subscribed to this thread.Message
ID: ***@***.***>
|
Beta Was this translation helpful? Give feedback.
-
As suggested by @skoudoro it was a basis mismatch. Difference in spherical harmonic basesThe basis used by dipy is the first (legacy) The basis used by mrtrix can be found here. It exists in dipy as However, dipy (v1.7.0 at the time of writing) is locked into the legacy
Converting between dipy and mrtrix representationsFollowing the convention in the dipy and mrtrix documentation we write and the dipy basis is given as The relation could be viewed as a simple re-ordering of basis functions: To be clear: the ordering of the Here is a function that provides the re-ordering as a permutation (a list of indices): from dipy.reconst.shm import sph_harm_ind_list
def get_dipy_to_mrtrix_permutation(sh_order):
m,l = sph_harm_ind_list(sh_order)
basis_indices = list(zip(l,m)) # dipy basis ordering
basis_indices_permuted = list(zip(l,-m)) # mrtrix basis ordering
permutation = [basis_indices.index(basis_indices_permuted[i]) for i in range(len(basis_indices))]
return permutation The CSD snippet in my original question can then be modified as follows to save the CSD results in a format that mrtrix mrview will interpret correctly: # (load dwi, WM mask, gradient table, response function)
csd_model = ConstrainedSphericalDeconvModel(gtab, response_mrtrix, sh_order=8)
csd_fit = csd_model.fit(data, mask=mask_data)
csd_shm_coeff = csd_fit.shm_coeff
csd_shm_coeff_mrtrix = csd_shm_coeff[:,:,:,get_dipy_to_mrtrix_permutation(8)]
save_nifti('output.nii.gz', csd_shm_coeff_mrtrix, affine, img.header) # (where affine and img.header are taken from the original dwi) Since the change of basis is idempotent (it's just a permutation swapping |
Beta Was this translation helpful? Give feedback.
As suggested by @skoudoro it was a basis mismatch.
Difference in spherical harmonic bases
The basis used by dipy is the first (legacy)
descoteaux07
basis described here, with code here.The basis used by mrtrix can be found here. It exists in dipy as
dipy.reconst.shm.real_sh_tournier
withlegacy=False
.However, dipy (v1.7.0 at the time of writing) is locked into the legacy
descoteaux07
basis:SphHarmModel
, basis function values are sampled from the legacydescoteaux07
basis. This means that when we use dipy to visualize FODs based on spherical harmonic coefficients, it will always assume we were using this basis.ConstrainedSphericalDeconvModel
, the legacydescoteaux07
basis is for…