def test_prepare_fieldmap_2_echoes(self): """Test 2 echoes works.""" # Import 2 echoes and rescale fname_phase1 = os.path.join(__dir_testing__, 'sub-fieldmap', 'fmap', 'sub-fieldmap_phase1.nii.gz') nii_phase1 = nib.load(fname_phase1) phase1 = (nii_phase1.get_fdata() * 2 * math.pi / 4095) - math.pi fname_phase2 = os.path.join(__dir_testing__, 'sub-fieldmap', 'fmap', 'sub-fieldmap_phase2.nii.gz') nii_phase2 = nib.load(fname_phase2) phase2 = (nii_phase2.get_fdata() * 2 * math.pi / 4095) - math.pi # Load mag data to speed it prelude fname_mag = os.path.join(__dir_testing__, 'sub-fieldmap', 'fmap', 'sub-fieldmap_magnitude1.nii.gz') mag = nib.load(fname_mag).get_fdata() echo_times = [0.0025, 0.0055] fieldmap = prepare_fieldmap([phase1, phase2], echo_times, nii_phase1.affine, mag=mag) assert fieldmap.shape == phase1.shape
def prepare_fieldmap_cli(phase, fname_mag, unwrapper, fname_output, fname_mask, threshold, gaussian_filter, sigma): """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. phase: Input path of phase nifti file(s), in ascending order: echo1, echo2, etc. """ # Import phase list_phase = [] echo_times = [] for i_echo in range(len(phase)): nii_phase, json_phase, phase_img = read_nii(phase[i_echo], auto_scale=True) # Add pi since read_nii returns phase between 0 and 2pi whereas prepare_fieldmap accepts between -pi to pi phase_img -= math.pi list_phase.append(phase_img) # Special case for echo_times if input is a phasediff if len(phase) == 1: # Check that the input phase is indeed a phasediff, by checking the existence of two echo times in the # metadata if not ('EchoTime1' in json_phase) or not ('EchoTime2' in json_phase): raise RuntimeError( "The JSON file of the input phase should include the fields EchoTime1 and EchoTime2 if" "it is a phase difference.") echo_times = [json_phase['EchoTime1'], json_phase['EchoTime2']] # [s] else: echo_times.append(json_phase['EchoTime']) # Get affine from nii affine = nii_phase.affine # If fname_mag is not an input define mag as None if fname_mag is not None: mag = nib.load(fname_mag).get_fdata() else: mag = None # Import mask if fname_mask is not None: mask = nib.load(fname_mask).get_fdata() else: mask = None fieldmap_hz = prepare_fieldmap(list_phase, echo_times, affine, mag=mag, unwrapper=unwrapper, mask=mask, threshold=threshold, gaussian_filter=gaussian_filter, sigma=sigma) # Save NIFTI nii_fieldmap = nib.Nifti1Image(fieldmap_hz, affine) nib.save(nii_fieldmap, fname_output) # Save json json_fieldmap = json_phase if len(phase) > 1: for i_echo in range(len(echo_times)): json_fieldmap[f'EchoTime{i_echo + 1}'] = echo_times[i_echo] fname_json = fname_output.rsplit('.nii', 1)[0] + '.json' with open(fname_json, 'w') as outfile: json.dump(json_fieldmap, outfile, indent=2)
def test_prepare_fieldmap_with_mag(self): """Test mag works.""" fieldmap = prepare_fieldmap([self.phase], self.echo_times, self.affine, mag=self.mag) assert fieldmap.shape == self.phase.shape
def test_prepare_fieldmap_1_echo(self): """Test default works.""" fieldmap = prepare_fieldmap([self.phase], self.echo_times, self.affine) assert fieldmap.shape == self.phase.shape # If the behaviour of the called function is modified, this assertion below should capture it: assert np.all( np.isclose( fieldmap[30:35, 40, 0, 0], np.array([ 18.51355514, 13.84794053, 9.48013154, 5.11232207, 0.64524454 ])))
def test_prepare_fieldmap_wrong_range(self): """Test error when range is not between -pi and pi.""" # This should return an error try: fieldmap = prepare_fieldmap([self.phase - math.pi], self.echo_times, self.affine) 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("\nRange is not between -pi and pi but does not throw an error.") assert False
def test_prepare_fieldmap_phasediff_1_echotime(self): """EchoTime of length one for phasediff should fail.""" # This should return an error try: fieldmap = prepare_fieldmap([self.phase], [self.echo_times[0]], self.affine) 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("\necho_time has the wrong shape but does not throw an error.") assert False
def test_prepare_fieldmap_wrong_echo_times(self): """Wrong number of echo times.""" echo_times = [0.001, 0.002, 0.003] # This should return an error try: fieldmap = prepare_fieldmap([self.phase], echo_times, self.affine) 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( "\nEcho_times has too many elements but does not throw an error.") assert False
def test_prepare_fieldmap_3_echoes(self): """3 echoes are not implemented so the test should fail.""" echo_times = [0.001, 0.002, 0.003] # This should return an error try: fieldmap = prepare_fieldmap([self.phase, self.phase, self.phase], echo_times, self.affine) 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("\n3 echoes are not implemented.") assert False
def test_prepare_fieldmap_mask_wrong_shape(self): """Mask has the wrong shape.""" # This should return an error try: fieldmap = prepare_fieldmap([self.phase], self.echo_times, self.affine, mask=np.zeros_like([5, 5])) 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("\nMask has the wrong shape but does not throw an error.") assert False
def test_prepare_fieldmap_gaussian_filter(self): """ Test output of gaussian filter optional argument""" fieldmap = prepare_fieldmap([self.phase], self.echo_times, self.affine, gaussian_filter=True, sigma=1) assert fieldmap.shape == self.phase.shape # If the behaviour of the called function is modified, this assertion below should capture it: assert np.all( np.isclose( fieldmap[30:35, 40, 0, 0], np.array([ 19.46307638, 15.46251356, 11.05021768, 6.28096375, 1.30868717 ])))