Skip to content
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

Add 3D wave caipi #38

Merged
merged 7 commits into from
Oct 11, 2023
Merged

Add 3D wave caipi #38

merged 7 commits into from
Oct 11, 2023

Conversation

Daval-G
Copy link
Collaborator

@Daval-G Daval-G commented Oct 9, 2023

This PR adds the 3D Wave-CAIPI trajectory based on the work from Bilgic, Berkin, Borjan A. Gagoski, Stephen F. Cauley, Audrey P. Fan, Jonathan R. Polimeni, P. Ellen Grant, Lawrence L. Wald, and Kawin Setsompop. "Wave‐CAIPI for highly accelerated 3D imaging." Magnetic resonance in medicine 73, no. 6 (2015): 2152-2162. This implementation includes additional features:

  • a parameter to choose the packing method, defining how the different helices should be positioned (i.e. following square or hexagonal tiling, over concentric circles or randomly)
  • a parameter to choose the shape covered over the kx-ky plane (circle, square, diamond or anything defined through p-norms)

Hereafter is a simple rendering with a square tiling of a circle, shown in 3D and over the kx-ky 2D plane.

image

image

@chaithyagr
Copy link
Member

chaithyagr commented Oct 10, 2023 via email

@philouc
Copy link
Collaborator

philouc commented Oct 10, 2023

Impressive Guillaume! Many thanks

@paquiteau
Copy link
Member

Nice work !

I think we should also propose the canonical way of reconstructing data acquired with this trajectory (e.g FFTs and PSF deconvolution) even if its only using a dummy numpy backend, and this also open the possibility to compare it with a NUFFT based reconstruction.

@Daval-G
Copy link
Collaborator Author

Daval-G commented Oct 10, 2023

Nice work !

I think we should also propose the canonical way of reconstructing data acquired with this trajectory (e.g FFTs and PSF deconvolution) even if its only using a dummy numpy backend, and this also open the possibility to compare it with a NUFFT based reconstruction.

That's a great suggestion for a future PR. I don't personally plan to work on that soon, should we create an issue to keep that in mind ?

@Daval-G Daval-G requested a review from paquiteau October 11, 2023 06:40
Copy link
Member

@paquiteau paquiteau left a comment

Choose a reason for hiding this comment

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

Just a few comments...

For the FFT I have already some code pending in pysap-fmri, that would be happy to move here (with sense support etc...). For the PSF stuff I guess we would need to find some existing implementation to adapt.

Comment on lines 242 to 252
# Ruff doesn't like lambdas
def _sort_main(x):
return nl.norm(x, ord=order)

def _sort_tie(x):
return nl.norm(x, ord=tie_order)

positions = np.array(positions) * spacing
positions = sorted(positions, key=_sort_tie)
positions = sorted(positions, key=_sort_main)
positions = positions[:Nc]
Copy link
Member

Choose a reason for hiding this comment

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

A functools.partial will do the trick (needs to be imported on top though)

Suggested change
# Ruff doesn't like lambdas
def _sort_main(x):
return nl.norm(x, ord=order)
def _sort_tie(x):
return nl.norm(x, ord=tie_order)
positions = np.array(positions) * spacing
positions = sorted(positions, key=_sort_tie)
positions = sorted(positions, key=_sort_main)
positions = positions[:Nc]
positions = np.array(positions) * spacing
positions = sorted(positions, key=partial(nl.norm, ord=order))
positions = sorted(positions, key=partial(nl.norm, ord=tie_order))
positions = positions[:Nc]

Comment on lines 599 to 608
if not isinstance(shape, str):
return shape
elif shape in ["square"]:
return np.inf
elif shape in ["circle", "circular"]:
return 2
elif shape in ["rhombus", "diamond"]:
return 1
else:
raise NotImplementedError(f"Unknown shape name: {shape}")
Copy link
Member

Choose a reason for hiding this comment

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

do we really need the redundancy of "circle/circular" and "rhombus/diamon" ? a single possibility would leads to less ambiguity.

Suggested change
if not isinstance(shape, str):
return shape
elif shape in ["square"]:
return np.inf
elif shape in ["circle", "circular"]:
return 2
elif shape in ["rhombus", "diamond"]:
return 1
else:
raise NotImplementedError(f"Unknown shape name: {shape}")
SHAPE = {"circle": 2, "square": np.inf, "diamond": 1} # an enum would be overkill
if not isinstance(shape,str):
return shape
try:
return SHAPE[shape]
except KeyError as e:
raise Value(f"Unknown shape name: {shape}") from e

@Daval-G Daval-G requested a review from paquiteau October 11, 2023 13:27
@paquiteau paquiteau merged commit 5b45df3 into mind-inria:master Oct 11, 2023
4 checks passed
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.

4 participants