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

Deepro dev #1

Merged
merged 16 commits into from
Jul 21, 2022
Merged

Deepro dev #1

merged 16 commits into from
Jul 21, 2022

Conversation

TerminalVelocityDPro
Copy link
Owner

Wrote eval_net_motion.py (which is a edited copy of eval_net.py) to run inference on test set scans with 2D motion corruption via the methodology outlined in previous Google Colabs. To do this, motion sweep values were removed from eval_net_motion.py and build_recon_val_loader was modified to induce 2D Motion Corruption. This was done by modifying the build_recon_val_loader function in meddlr/data/build.py by adding angle, translation, nshots, and trajectory parameters and by replacing DataTransform with a newly made MotionDataTransform that takes in the angle, translation, nshots, and trajectory values specified in the parameters. MotionDataTransform is written in meddlr/data/transforms/transform.py and copies much of the original DataTransform. This biggest and most important change is the replacement of MotionModel with MotionModel2D, which takes in nshots, angle, translation, and trajectory values specified. MotionModel2D conducts the 2D Motion Corruption and is written in meddlr/data/transforms/2D_motion.py. It copies largely from MotionModel but simulates 2D motion for multi-shot Cartesian MRI instead.

Notes:
This is completely untested and undebugged code.

Please let me know what you think / what I need to fix / what I could improve. In addition, let me know if there are any places that are confusing and that need better commenting, or if there are areas that need better documentation.

Thanks,
Deepro.

@TerminalVelocityDPro TerminalVelocityDPro self-assigned this Jul 10, 2022
Comment on lines 252 to 255
angle: float = 0,
translation: float = 0,
nshots: int = 0,
trajectory: str = "blocked",
Copy link
Collaborator

Choose a reason for hiding this comment

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

we want to avoid taking in too many arguments - consider passing in a data_transform

Copy link
Owner Author

Choose a reason for hiding this comment

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

If I pass in just a data_transform as a parameter (for instance like MotionDataTransform), how would I pass in the parameters for angle, translation, nshots, and trajectory into the MotionDataTransform? Thanks, Deepro.

This module is responsible for simulating different motion artifacts.
"""

def __init__(self, nshots, angle, translate, trajectory, seed=None):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Add type annotations. For example nshots: int

This should be equivalent to ceil(phase_encode_dim /
echo_train_length).
angle : The (min, max) angle for rotation. Values should be in
degrees and should be >=-180, <=180. Use 'None' to
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: use range notation instead [-180, 180]

Copy link
Collaborator

Choose a reason for hiding this comment

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

if this can be None, make sure it is listed as an optional variable: angle: Optional[Tuple[int, int]]. Given that it is optional, it should default to None

translate: The fraction of (height, width) to translate.
e.g. 0.1 => 10% of the corresponding dimension.
So (0.1, 0.2) => 10% of height, 20% of width.
Use 'None' to ignore translation.
Copy link
Collaborator

Choose a reason for hiding this comment

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

See comment for angle

Comment on lines 50 to 58
This function supports two trajectories:
- 'blocked' : Where each shot corresponds to a consecutive block of
kspace. (e.g. 1 1 2 2 3 3)
- 'interleaved' : Where shots are interleaved (e.g. 1 2 3 1 2 3)

We assume the phase encode direction is left to right (i.e. along
width dimesion).

TODO: Add support for sensitivity maps.
Copy link
Collaborator

Choose a reason for hiding this comment

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

these comments don't apply for this function - move them to the class docstring level

add_motion: bool = False,
angle: float = 0,
translation: float = 0,
nshots: int = 0,
Copy link
Collaborator

Choose a reason for hiding this comment

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

is a default value of 0 ever valid? if not, set it to None and raise a ValueError if it is not set

Copy link
Owner Author

Choose a reason for hiding this comment

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

@ad12 Should this ValueError be raised if a parameter is set to None? Sorry, just trying to better understand. I added an Optional to the angle and translation parameters for the motion corruption function, so the function can handle None parameters for angle and translation right? So, would I still raise that ValueError? Or do I raise it for something else?

Copy link
Owner Author

Choose a reason for hiding this comment

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

Would nshots be checked to see if it was set? And thus would that raise a ValueError? Also, would the type annotation for nshots in this function be just nshots: int? Thanks, Deepro.

@@ -364,3 +365,211 @@ def __call__(self, kspace, maps, target, fname, slice_id, is_fixed, acceleration
target = target.squeeze(0)

return masked_kspace, maps, target, mean, std, norm


class MotionDataTransform:
Copy link
Collaborator

Choose a reason for hiding this comment

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

We are only going to use this transform for inference right now - only keep the logic that is relevant when is_test=True

Copy link
Owner Author

Choose a reason for hiding this comment

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

Should something be done to handle for when is_test is False? Or is that not necessary for now? Thanks.

Comment on lines 440 to 479
def _call_augmentor(
self, kspace, maps, target, fname, slice_id, is_fixed, acceleration: int = None
):
assert not self._is_test, "Augmentor is not supported with testing yet"

# Convert everything from numpy arrays to tensors
kspace = cplx.to_tensor(kspace).unsqueeze(0)
maps = cplx.to_tensor(maps).unsqueeze(0)
target_init = cplx.to_tensor(target).unsqueeze(0)
target = (
torch.complex(target_init, torch.zeros_like(target_init)).unsqueeze(-1)
if not torch.is_complex(target_init)
else target_init
) # handle rss vs. sensitivity-integrated
norm = torch.sqrt(torch.mean(cplx.abs(target) ** 2))

seed = sum(tuple(map(ord, fname))) if self._is_test or is_fixed else None # noqa
mask_gen = partial(
self._subsampler.__call__, mode="2D", seed=seed, acceleration=acceleration
)

out, _, _ = self.augmentor(
kspace,
maps=maps,
target=target,
normalizer=self._normalizer,
mask_gen=mask_gen,
skip_tfm=is_fixed, # Skip augmentations for unsupervised scans.
)
masked_kspace = out["kspace"]
maps = out["maps"]
target = out["target"]
mean = out["mean"]
std = out["std"]

# Get rid of batch dimension...
masked_kspace = masked_kspace.squeeze(0)
maps = maps.squeeze(0)
target = target.squeeze(0)

Copy link
Collaborator

Choose a reason for hiding this comment

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

i dont think you need this function for your transform class

add_motion = self.add_motion and (
self._is_test or (not is_fixed and self.rng.uniform() < self.p_motion)
)
if add_motion:
Copy link
Collaborator

Choose a reason for hiding this comment

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

motion simulation should happen before the undersampling

Copy link
Owner Author

Choose a reason for hiding this comment

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

Where should the noise simulation go? Before or after the undersampling? Thanks, Deepro.

tools/eval_net_motion.py Show resolved Hide resolved
@ad12
Copy link
Collaborator

ad12 commented Jul 11, 2022

Also make sure to run the linter and formatter - run make autoformat from the meddlr repository folder

meddlr/data/build.py Outdated Show resolved Hide resolved
meddlr/data/build.py Outdated Show resolved Hide resolved
meddlr/data/transforms/motion_corruption_2D.py Outdated Show resolved Hide resolved
meddlr/data/transforms/transform.py Show resolved Hide resolved
meddlr/data/transforms/transform.py Outdated Show resolved Hide resolved
meddlr/data/transforms/transform.py Show resolved Hide resolved
tools/eval_net.py Outdated Show resolved Hide resolved
@TerminalVelocityDPro TerminalVelocityDPro merged commit 44dc2b6 into 2D-motion-corruption-main Jul 21, 2022
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