def test_read_nii_b1(self): fname_b1 = os.path.join(__dir_testing__, 'b1_maps', 'nifti', 'sub-01_run-10_TB1map.nii.gz') nii, json_info, b1 = read_nii(fname_b1) assert b1.shape == (64, 64, 16, 8), "Wrong rf-map shape" assert np.abs(b1).max() <= 180 and np.abs( b1).min() >= 0, "Magnitude values out of range" assert np.angle(b1).max(initial=0) <= np.pi and np.angle(b1).min(initial=0) >= -np.pi,\ "Phase values out of range" # Check masking consistency for all coils at each slice for i in range(b1.shape[2]): for j in range(b1.shape[3] - 1): assert ((b1[:, :, i, j] != 0) == (b1[:, :, i, j + 1] != 0)).any() test_values = [ -4.274539911369111 + 4.599952786001116j, -5.8027003257021725 + 2.2042390773527423j, -2.1929304691258276 + 1.5241263801971388j ] assert np.isclose( [b1[35, 35, 0, 0], b1[35, 35, 6, 7], b1[40, 25, 15, 7]], test_values).all() assert (json.dumps(json_info, sort_keys=True) == json.dumps(self._json_b1, sort_keys=True)),\ "JSON file is not correctly loaded for first RF JSON"
def test_read_nii_b1_negative_mag(self): data_negative_mag = self._data_b1.copy() data_negative_mag[35, 35, 0, 0] = -1 dummy_data_b1 = nib.nifti1.Nifti1Image(dataobj=data_negative_mag, affine=self._aff) nib.save(dummy_data_b1, os.path.join(self.data_path_b1, 'dummy_b1_negative_mag')) with open( os.path.join(self.data_path_b1, 'dummy_b1_negative_mag.json'), 'w') as json_file: json.dump(self._json_b1, json_file) fname_b1 = os.path.join(self.data_path_b1, "dummy_b1_negative_mag.nii") with pytest.raises(ValueError, match="Unexpected negative magnitude values"): read_nii(fname_b1)
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_read_nii_b1_no_shimsetting(self): dummy_data_b1 = nib.nifti1.Nifti1Image(dataobj=self._data_b1, affine=self._aff) nib.save(dummy_data_b1, os.path.join(self.data_path_b1, 'dummy_b1_no_shimsetting')) with open( os.path.join(self.data_path_b1, 'dummy_b1_no_shimsetting.json'), 'w') as json_file: self._json_b1_no_shimsetting = self._json_b1.copy() del self._json_b1_no_shimsetting['ShimSetting'] json.dump(self._json_b1_no_shimsetting, json_file) fname_b1 = os.path.join(self.data_path_b1, 'dummy_b1_no_shimsetting.nii') with pytest.raises(KeyError, match="Missing json tag: 'ShimSetting'"): read_nii(fname_b1)
def test_read_nii_b1_no_scaling(self): fname_b1 = os.path.join(__dir_testing__, 'b1_maps', 'nifti', 'sub-01_run-10_TB1map.nii.gz') _, _, b1 = read_nii(fname_b1, auto_scale=False) assert b1.shape == (64, 64, 16, 16), "Wrong rf-map shape" test_values = [87.0, 1890.0, 37.0] assert [b1[35, 35, 0, 0], b1[35, 35, 6, 13], b1[40, 25, 15, 7]] == test_values
def test_read_nii_b1_negative_mag(self): data_negative_mag = self._data_b1.copy() data_negative_mag[35, 35, 0, 0] = -1 dummy_data_b1 = nib.nifti1.Nifti1Image(dataobj=data_negative_mag, affine=self._aff) nib.save(dummy_data_b1, os.path.join(self.data_path_b1, 'dummy_b1_negative_mag')) with open( os.path.join(self.data_path_b1, 'dummy_b1_negative_mag.json'), 'w') as json_file: json.dump(self._json_b1, json_file) fname_b1 = os.path.join(self.data_path_b1, "dummy_b1_negative_mag.nii") try: read_nii(fname_b1) except ValueError: return 0
def test_read_nii_real_data(self): fname_phasediff = os.path.join(__dir_testing__, 'realtime_zshimming_data', 'nifti', 'sub-example', 'fmap', 'sub-example_phasediff.nii.gz') nii, json_info, phasediff = read_nii(fname_phasediff) assert nii.shape == (64, 96, 1, 10) assert ('P' in json_info['ImageType']) assert (phasediff.max() <= 2 * math.pi) and (phasediff.min() >= 0)
def test_read_nii_b1_no_slicetiming(self): dummy_data_b1 = nib.nifti1.Nifti1Image(dataobj=self._data_b1, affine=self._aff) nib.save(dummy_data_b1, os.path.join(self.data_path_b1, 'dummy_b1_no_slicetiming')) with open( os.path.join(self.data_path_b1, 'dummy_b1_no_slicetiming.json'), 'w') as json_file: self._json_b1_no_slicetiming = self._json_b1.copy() del self._json_b1_no_slicetiming['SliceTiming'] json.dump(self._json_b1_no_slicetiming, json_file) fname_b1 = os.path.join(self.data_path_b1, 'dummy_b1_no_slicetiming.nii') try: read_nii(fname_b1) except ValueError: return 0
def test_read_nii_b1_wrong_slicetiming(self): dummy_data_b1 = nib.nifti1.Nifti1Image(dataobj=self._data_b1, affine=self._aff) nib.save(dummy_data_b1, os.path.join(self.data_path_b1, 'dummy_b1_wrong_slicetiming')) with open( os.path.join(self.data_path_b1, 'dummy_b1_wrong_slicetiming.json'), 'w') as json_file: self._json_b1_wrong_slicetiming = self._json_b1.copy() self._json_b1_wrong_slicetiming['SliceTiming'] = str(np.zeros([15 ])) json.dump(self._json_b1_wrong_slicetiming, json_file) fname_b1 = os.path.join(self.data_path_b1, "dummy_b1_wrong_slicetiming.nii") with pytest.raises( ValueError, match="Wrong array dimension: number of slices not matching"): read_nii(fname_b1)
def test_read_nii_b1_wrong_shimsetting(self): dummy_data_b1 = nib.nifti1.Nifti1Image(dataobj=self._data_b1, affine=self._aff) nib.save(dummy_data_b1, os.path.join(self.data_path_b1, 'dummy_b1_wrong_shimsetting')) with open( os.path.join(self.data_path_b1, 'dummy_b1_wrong_shimsetting.json'), 'w') as json_file: self._json_b1_wrong_shimsetting = self._json_b1.copy() self._json_b1_wrong_shimsetting['ShimSetting'] = str(np.zeros([15 ])) json.dump(self._json_b1_wrong_shimsetting, json_file) fname_b1 = os.path.join(self.data_path_b1, "dummy_b1_wrong_shimsetting.nii") try: read_nii(fname_b1) except ValueError: return 0
def test_read_nii_b1_no_slicetiming(self): dummy_data_b1 = nib.nifti1.Nifti1Image(dataobj=self._data_b1, affine=self._aff) nib.save(dummy_data_b1, os.path.join(self.data_path_b1, 'dummy_b1_no_slicetiming')) with open( os.path.join(self.data_path_b1, 'dummy_b1_no_slicetiming.json'), 'w') as json_file: self._json_b1_no_slicetiming = self._json_b1.copy() del self._json_b1_no_slicetiming['SliceTiming'] json.dump(self._json_b1_no_slicetiming, json_file) fname_b1 = os.path.join(self.data_path_b1, "dummy_b1_no_slicetiming.nii") with pytest.warns( UserWarning, match= "Missing json tag: 'SliceTiming', slices number cannot be checked." ): read_nii(fname_b1)