Module ukat.vessels.phase_contrast
The velocity and flow calculations in this Phase Contrast class are based on the scientific paper:
"Phase-contrast magnetic resonance imaging to assess renal perfusion: a systematic review and statement paper"
Giulia Villa, Steffen Ringgaard, Ingo Hermann, Rebecca Noble, Paolo Brambilla, Dinah S. Khatir, Frank G. Zöllner, Susan T. Francis, Nicholas M. Selby, Andrea Remuzzi, Anna Caroli
Magnetic Resonance Materials in Physics, Biology and Medicine (2020) 33:3-21 https://doi.org/10.1007/s10334-019-00772-0
Functions
def convert_to_velocity(pixel_array, velocity_encoding, velocity_encode_scale=None)
-
Calculate the velocity array from the given input image and velocity encoding. If a velocity encode scale is given then it is used to convert the pixel_value to radians.
Parameters
pixel_array
:np.ndarray
- A 3D array containing the phase images of the phase contrast sequence.
velocity_encoding
:float
- The value of the velocity encoding in cm/s.
velocity_encode_scale
:float
, optional- If given, this value is used to scale from image intensity to radians.
Returns
velocity_array
:np.ndarray
- A 3D array containing the velocity images of the phase contrast sequence, i.e. the dimensions of the array are [x, y, p], where p corresponds to the phase (or trigger delay).
Classes
class PhaseContrast (velocity_array, affine, mask)
-
Generates velocity and flow measurements of an MRI Phase Contrast Sequence.
Attributes
velocity_array
:np.ndarray
- The input velocity array masked.
shape
:tuple
- The shape of the velocity array.
pixel_spacing
:tuple
- The pixel spacing of the acquisition estimated from the affine.
mask
:np.ndarray
- A boolean mask of the voxels to fit.
num_pixels
:np.ndarray
- List containing the number of True values in the mask per phase.
area
:np.ndarray
- List containing the area (cm2) of the mask per phase.
min_velocity
:np.ndarray
- List containing the minimum velocity values (cm/s) per phase.
mean_velocity
:np.ndarray
- List containing the average velocity values (cm/s) per phase.
max_velocity
:np.ndarray
- List containing the maximum velocity values (cm/s) per phase.
std_velocity
:np.ndarray
- List containing the std dev of the velocity values (cm/s) per phase.
rbf
:np.ndarray
- List containing the Renal Blood Flow values (ml/min) per phase.
mean_velocity_global
:float
- Average velocity (cm/s) across the different phases.
mean_rbf
:float
- Average Renal Blood Flow (ml/min) across the different phases.
resistive_index
:float
- A prognostic marker in renal vascular diseases which range is [0, 1].
Initialise a PhaseContrast class instance.
Parameters
velocity_array
:np.ndarray
- A 3D array containing the velocity images of the phase contrast sequence, i.e. the dimensions of the array are [x, y, p], where p corresponds to the phase (or trigger delay).
affine
:np.ndarray
- A matrix giving the relationship between voxel coordinates and world coordinates.
mask
:np.ndarray
- A boolean mask of the voxels to fit. Should be the shape of the raw data.
Expand source code
class PhaseContrast: """ Generates velocity and flow measurements of an MRI Phase Contrast Sequence. Attributes ---------- velocity_array : np.ndarray The input velocity array masked. shape : tuple The shape of the velocity array. pixel_spacing : tuple The pixel spacing of the acquisition estimated from the affine. mask : np.ndarray A boolean mask of the voxels to fit. num_pixels : np.ndarray List containing the number of True values in the mask per phase. area : np.ndarray List containing the area (cm2) of the mask per phase. min_velocity : np.ndarray List containing the minimum velocity values (cm/s) per phase. mean_velocity : np.ndarray List containing the average velocity values (cm/s) per phase. max_velocity : np.ndarray List containing the maximum velocity values (cm/s) per phase. std_velocity : np.ndarray List containing the std dev of the velocity values (cm/s) per phase. rbf : np.ndarray List containing the Renal Blood Flow values (ml/min) per phase. mean_velocity_global : float Average velocity (cm/s) across the different phases. mean_rbf : float Average Renal Blood Flow (ml/min) across the different phases. resistive_index : float A prognostic marker in renal vascular diseases which range is [0, 1]. """ def __init__(self, velocity_array, affine, mask): """Initialise a PhaseContrast class instance. Parameters ---------- velocity_array : np.ndarray A 3D array containing the velocity images of the phase contrast sequence, i.e. the dimensions of the array are [x, y, p], where p corresponds to the phase (or trigger delay). affine : np.ndarray A matrix giving the relationship between voxel coordinates and world coordinates. mask : np.ndarray A boolean mask of the voxels to fit. Should be the shape of the raw data. """ self.shape = velocity_array.shape self.affine = affine self.pixel_spacing = (np.linalg.norm(self.affine[:3, 1]), np.linalg.norm(self.affine[:3, 0])) self.mask = np.where(mask, mask, np.nan) self.velocity_array = np.abs(velocity_array * self.mask) self.num_pixels = [] self.area = [] self.min_velocity = [] self.mean_velocity = [] self.max_velocity = [] self.std_velocity = [] self.rbf = [] self.mean_velocity_global = 0 self.mean_rbf = 0 self.resistive_index = 0 if len(self.shape) == 3: # Extract number pixels, area, velocity stats (min, mean, max, std) # and renal blood flow (RBF) self.num_pixels = np.count_nonzero(~np.isnan(self.velocity_array), axis=(0, 1)) # area = (num_pixels * mm * mm * 0.01) = cm2 self.area = (self.num_pixels * self.pixel_spacing[0] * self.pixel_spacing[1] * 0.01) self.min_velocity = np.nanmin(self.velocity_array, axis=(0, 1)) self.mean_velocity = np.nanmean(self.velocity_array, axis=(0, 1)) self.max_velocity = np.nanmax(self.velocity_array, axis=(0, 1)) self.std_velocity = np.nanstd(self.velocity_array, axis=(0, 1)) # q = (60 * cm2 * cm/s) = ml/min self.rbf = 60 * np.array(self.area * self.mean_velocity) # Mean velocity global and mean flow self.mean_velocity_global = np.mean(self.mean_velocity) self.mean_rbf = np.mean(self.rbf) # Restrictive Index mean_velocity_systole = np.max(self.mean_velocity) mean_velocity_diastole = np.min(self.mean_velocity) self.resistive_index = ((mean_velocity_systole - mean_velocity_diastole) / mean_velocity_systole) # Convert any nan values to 0 self.velocity_array = np.nan_to_num(self.velocity_array) self.mask = np.nan_to_num(self.mask) else: raise ValueError('The input velocity_array should be 3D.') def get_stats_table(self): """ Stores most of PhaseContrast class attributes into a pandas DataFrame. Returns ---------- table : pandas.DataFrame Returns a table with the results/stats of each output per phase. """ stats = {"RBF (ml/min)": self.rbf, "Area (cm2)": self.area, "Nr Pixels": self.num_pixels, "Mean Vel (cm/s)": self.mean_velocity, "Min Vel (cm/s)": self.min_velocity, "Max Vel (cm/s)": self.max_velocity, "StdDev Vel (cm/s)": self.std_velocity} table = pd.DataFrame(data=stats) return table def print_stats(self): """ Prints the table with the stats for each output per phase. """ stats_table = self.get_stats_table() print(tabulate(stats_table, headers='keys', tablefmt='github', floatfmt='.3f')) def to_csv(self, path): """ Saves the stats_table into a csv file. Parameters ---------- path : str Path to the desired csv file. """ stats_table = self.get_stats_table() stats_table.to_csv(path) def plot(self, stat='default', file_name=None): """ This method plots the output PhaseContrast stats per phase. Parameters ---------- stat : str, optional Name of the output stat variable. This method plots mean velocity and the RBF by default. file_name : str, optional Path to the image file (*.jpg, *.png, etc.) in which the plot will be saved. """ if stat == 'default': fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 10)) ax1.plot(self.mean_velocity, 'ro-') ax1.set_ylabel('Velocity (cm/sec)') ax1.set_xlabel('Phase') ax1.set_title('Average Velocity') ax2.plot(self.rbf, 'b-') ax2.set_ylabel('RBF (ml/min)') ax2.set_xlabel('Phase') ax2.set_title('Renal Artery Blood Flow') else: if stat == 'min_velocity': stat_variable = self.min_velocity y_label = 'Velocity (cm/sec)' title = 'Minimum Velocity' elif stat == 'mean_velocity': stat_variable = self.mean_velocity y_label = 'Velocity (cm/sec)' title = 'Average Velocity' elif stat == 'max_velocity': stat_variable = self.max_velocity y_label = 'Velocity (cm/sec)' title = 'Maximum Velocity' elif stat == 'std_velocity': stat_variable = self.std_velocity y_label = 'Velocity (cm/sec)' title = 'Standard Deviation of the Velocity' elif stat == 'rbf': stat_variable = self.rbf y_label = 'RBF (ml/min)' title = 'Renal Artery Blood Flow' elif stat == 'num_pixels': stat_variable = self.num_pixels y_label = '# Pixels' title = 'Number of Pixels in the Region Of Interest (ROI)' elif stat == 'area': stat_variable = self.area y_label = 'Area ($cm^2$)' title = 'Area of the Region Of Interest (ROI)' else: raise ValueError('The stat provided is not valid.') fig, ax = plt.subplots(figsize=(10, 10)) ax.plot(stat_variable, 'ro-') ax.set_ylabel(y_label) ax.set_xlabel('Phase') ax.set_title(title) # The following saves the plot(s) to file_name, if given. if file_name is not None: fig.savefig(file_name) def to_nifti(self, output_directory=os.getcwd(), base_file_name='Output', maps='all'): """Exports the velocity array and the renal artery mask to NIfTI. Parameters ---------- output_directory : string, optional Path to the folder where the NIfTI files will be saved. base_file_name : string, optional Filename of the resulting NIfTI. This code appends the extension. Eg., base_file_name = 'Output' will result in 'Output.nii.gz'. maps : list or 'all', optional List of maps to save to NIfTI. This should either the string "all" or a list of maps from ["phase_array", "mask", "velocity_array"]. """ os.makedirs(output_directory, exist_ok=True) base_path = os.path.join(output_directory, base_file_name) if maps == 'all' or maps == ['all']: maps = ["velocity_array", "mask"] if isinstance(maps, list): for result in maps: if result == 'velocity_array' or result == 'velocity array': velocity_nifti = nib.Nifti1Image(self.velocity_array, affine=self.affine) nib.save(velocity_nifti, base_path + '_velocity_array.nii.gz') elif result == 'mask': mask_nifti = nib.Nifti1Image(self.mask.astype(np.uint16), affine=self.affine) nib.save(mask_nifti, base_path + '_mask.nii.gz') else: raise ValueError('No NIfTI file saved. The variable "maps" ' 'should be "all" or a list of maps from ' '"["velocity_array", "mask"]".')
Methods
def get_stats_table(self)
-
Stores most of PhaseContrast class attributes into a pandas DataFrame.
Returns
table
:pandas.DataFrame
- Returns a table with the results/stats of each output per phase.
def plot(self, stat='default', file_name=None)
-
This method plots the output PhaseContrast stats per phase.
Parameters
stat
:str
, optional- Name of the output stat variable. This method plots mean velocity and the RBF by default.
file_name
:str
, optional- Path to the image file (.jpg, .png, etc.) in which the plot will be saved.
def print_stats(self)
-
Prints the table with the stats for each output per phase.
def to_csv(self, path)
-
Saves the stats_table into a csv file.
Parameters
path
:str
- Path to the desired csv file.
def to_nifti(self, output_directory='/home/runner/work/ukat/ukat', base_file_name='Output', maps='all')
-
Exports the velocity array and the renal artery mask to NIfTI.
Parameters
output_directory
:string
, optional- Path to the folder where the NIfTI files will be saved.
base_file_name
:string
, optional- Filename of the resulting NIfTI. This code appends the extension. Eg., base_file_name = 'Output' will result in 'Output.nii.gz'.
maps
:list
or'all'
, optional- List of maps to save to NIfTI. This should either the string "all" or a list of maps from ["phase_array", "mask", "velocity_array"].