def test_unwrap_phase_wrong_shape(self):
        """Input wrong shape."""

        # This should return an error
        try:
            unwrap_phase(np.expand_dims(self.phase, -1), self.affine, mag=self.mag)
        except RuntimeError:
            # If an exception occurs, this is the desired behaviour
            return 0

        # If there isn't an error, then there is a problem
        print('\nWrong dimensions but does not throw an error.')
        assert False
    def test_unwrap_phase_wrong_unwrapper(self):
        """Input wrong unwrapper."""

        # This should return an error
        try:
            unwrap_phase(self.phase, self.affine, mag=self.mag, unwrapper='Not yet implemented.')
        except NotImplementedError:
            # If an exception occurs, this is the desired behaviour
            return 0

        # If there isn't an error, then there is a problem
        print('\nNot supported unwrapper but does not throw an error.')
        assert False
 def test_unwrap_phase_prelude_2d_mask(self):
     """Test prelude with 2d mask parameter."""
     phase = self.phase[..., 0, 0]
     mag = self.mag[..., 0, 0]
     unwrapped = unwrap_phase(phase, self.affine, unwrapper='prelude', mag=mag, mask=np.ones_like(phase))
     assert unwrapped.shape == phase.shape
 def test_unwrap_phase_prelude_4d_mask(self):
     """Test prelude with mask parameter."""
     unwrapped = unwrap_phase(self.phase, self.affine, unwrapper='prelude', mag=self.mag,
                              mask=np.ones_like(self.phase))
     assert unwrapped.shape == self.phase.shape
 def test_unwrap_phase_prelude_threshold(self):
     """Test prelude with threshold parameter."""
     unwrapped = unwrap_phase(self.phase, self.affine, unwrapper='prelude', mag=self.mag, threshold=0.1)
     assert unwrapped.shape == self.phase.shape
 def test_unwrap_phase_prelude_2d(self):
     """Test prelude with 2d input data."""
     phase = self.phase[..., 0, 0]
     mag = self.mag[..., 0, 0]
     unwrapped = unwrap_phase(phase, self.affine, unwrapper='prelude', mag=mag)
     assert unwrapped.shape == phase.shape
 def test_unwrap_phase_prelude_4d(self):
     """Test prelude with 4d input data."""
     unwrapped = unwrap_phase(self.phase, self.affine, unwrapper='prelude', mag=self.mag)
     assert unwrapped.shape == self.phase.shape
Example #8
0
def prepare_fieldmap(phase,
                     echo_times,
                     affine,
                     unwrapper='prelude',
                     mag=None,
                     mask=None,
                     threshold=None):
    """ Creates fieldmap (in Hz) from phase images. This function accommodates multiple echoes (2 or more) and phase
    difference. This function also accommodates 4D phase inputs, where the 4th dimension represents the time, in case
    multiple field maps are acquired across time for the purpose of real-time shimming experiments.

    Args:
        phase (list): List of phase values in a numpy.ndarray. The numpy array can be [x, y], [x, y, z] or [x, y, z, t].
                      The values must range from [-pi to pi].
        echo_times (list): List of echo times in seconds for each echo. The number of echotimes must match the number of
                           echoes. It input is a phasediff (1 phase), input 2 echotimes.
        affine (numpy.ndarray): 4x4 affine matrix.
        unwrapper (str): Unwrapper to use for phase unwrapping. Supported: prelude.
        mag (numpy.ndarray): Array containing magnitude data relevant for ``phase`` input. Shape must match phase[echo].
        mask (numpy.ndarray): Mask for masking output fieldmap. Must match shape of phase[echo].
        threshold: Prelude parameter used for masking.

    Returns
        numpy.ndarray: Unwrapped fieldmap in Hz.
    """
    # Check inputs
    for i_echo in range(len(phase)):
        # Check that the output phase is in radian (Note: the test below is not 100% bullet proof)
        if (phase[i_echo].max() > math.pi) or (phase[i_echo].min() < -math.pi):
            raise RuntimeError("read_nii must range from -pi to pi.")

    # Check that the input echotimes are the appropriate size by looking at phase
    is_phasediff = (len(phase) == 1 and len(echo_times) == 2)
    if not is_phasediff:
        if len(phase) != len(echo_times) or (len(phase) == 1
                                             and len(echo_times) == 1):
            raise RuntimeError(
                "Phasediff must have 2 echotime points. Otherwise the number of echoes must match the"
                " number of echo times.")

    # Make sure mag is the right shape
    if mag is not None:
        if mag.shape != phase[0].shape:
            raise RuntimeError("mag and phase must have the same dimensions.")

    # Make sure mask has the right shape
    if mask is not None:
        if mask.shape != phase[0].shape:
            raise RuntimeError("Shape of mask and phase must match.")

    # Get the time between echoes and calculate phase difference depending on number of echoes
    if len(phase) == 1:
        # phase should be a phasediff
        phasediff = phase[0]
        echo_time_diff = echo_times[1] - echo_times[0]  # [s]

    elif len(phase) == 2:
        echo_0 = phase[0]
        echo_1 = phase[1]

        # Calculate phasediff using complex difference
        comp_0 = np.ones_like(echo_0) * np.exp(-1j * echo_0)
        comp_1 = np.ones_like(echo_1) * np.exp(1j * echo_1)
        phasediff = np.angle(comp_0 * comp_1)

        # Calculate the echo time difference
        echo_time_diff = echo_times[1] - echo_times[0]  # [s]

    else:
        # TODO: More echoes
        # TODO: Add method once multiple methods are implemented
        raise NotImplementedError(
            f"This number of phase input is not supported: {len(phase)}.")

    # Run the unwrapper
    phasediff_unwrapped = unwrap_phase(phasediff,
                                       affine,
                                       unwrapper=unwrapper,
                                       mag=mag,
                                       mask=mask,
                                       threshold=threshold)

    # TODO: correct for potential wraps between time points

    # Divide by echo time
    fieldmap_rad = phasediff_unwrapped / echo_time_diff  # [rad / s]
    fieldmap_hz = fieldmap_rad / (2 * math.pi)  # [Hz]

    return fieldmap_hz