Module ukat.qa.snr
Classes
class Isnr (pixel_array, affine, noise_mask=None, n_clusters=3)
-
Attributes
isnr
:float
- Image signal to noise ratio.
isnr_map
:np.ndarray
- A map of the image signal to noise ratio.
noise_mask
:np.ndarray
- Mask of the ROI used to measure noise levels. Should be voxels outside the body.
clusters
:np.ndarray
- The labels assigned to each voxel during automatic background segmentation. This can be useful for debugging.
Initialise an image signal to noise ratio (iSNR) class instance. Parameters
pixel_array
:np.ndarray
- Array of voxels over which iSNR should be calculated.
affine
:np.ndarray
- A matrix giving the relationship between voxel coordinates and world coordinates.
noise_mask
:np.ndarray
, optional- A binary voxel mask where voxels representing background i.e. outside the body, are True. If no mask is supplied, one is estimated using a Bayesian Gaussian mixture model to segment background voxels.
n_clusters
:int
, optional- When using the automatic background segmentation this is the total number of componenets the image is segmented into. The component with the lowest mean is assumed to be background. Default 3.
Expand source code
class Isnr: """ Attributes ---------- isnr : float Image signal to noise ratio. isnr_map : np.ndarray A map of the image signal to noise ratio. noise_mask : np.ndarray Mask of the ROI used to measure noise levels. Should be voxels outside the body. clusters : np.ndarray The labels assigned to each voxel during automatic background segmentation. This can be useful for debugging. """ def __init__(self, pixel_array, affine, noise_mask=None, n_clusters=3): """Initialise an image signal to noise ratio (iSNR) class instance. Parameters ---------- pixel_array : np.ndarray Array of voxels over which iSNR should be calculated. affine : np.ndarray A matrix giving the relationship between voxel coordinates and world coordinates. noise_mask : np.ndarray, optional A binary voxel mask where voxels representing background i.e. outside the body, are True. If no mask is supplied, one is estimated using a Bayesian Gaussian mixture model to segment background voxels. n_clusters : int, optional When using the automatic background segmentation this is the total number of componenets the image is segmented into. The component with the lowest mean is assumed to be background. Default 3. """ self.pixel_array = pixel_array self.affine = affine self.n_clusters = n_clusters self.shape = pixel_array.shape self.dimensions = len(pixel_array.shape) if noise_mask is None: self.__mask_background__() else: self.noise_mask = noise_mask self.clusters = np.nan self.isnr = np.nan self.isnr_map = np.zeros(self.shape) self.__snr__() def __mask_background__(self): np.random.seed(0) gmm = BayesianGaussianMixture(n_components=self.n_clusters, random_state=0, max_iter=500) # Because gmm's can get quite slow when fitting to large images, # we randomly sample a number of voxels equivalent to a 128 x 128 x # 3 image to keep runtimes consistent and manageable for large # multi-te/ti/dynamic images. fit_prop = (128 ** 2 * 3) / np.prod(self.shape) fit_mask = np.random.rand(self.pixel_array.size) < fit_prop gmm.fit(self.pixel_array.reshape(-1, 1)[fit_mask]) self.clusters = gmm.predict(self.pixel_array.reshape(-1, 1)).reshape( self.shape) bg_label = np.argmin(gmm.means_) self.noise_mask = self.clusters == bg_label def __snr__(self): noise = np.std(self.pixel_array[self.noise_mask]) signal = np.mean(self.pixel_array[~self.noise_mask]) self.isnr = (signal / noise) * np.sqrt(2 - (np.pi / 2)) self.isnr_map = (self.pixel_array / noise) * np.sqrt(2 - (np.pi / 2)) def to_nifti(self, output_directory=os.getcwd(), base_file_name='Output'): """Exports iSNR maps 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'. """ os.makedirs(output_directory, exist_ok=True) base_path = os.path.join(output_directory, base_file_name) isnr_nifti = nib.Nifti1Image(self.isnr_map, affine=self.affine) nib.save(isnr_nifti, base_path + '_isnr_map.nii.gz')
Methods
def to_nifti(self, output_directory='/home/runner/work/ukat/ukat', base_file_name='Output')
-
Exports iSNR maps 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'.
class Tsnr (pixel_array, affine, mask=None)
-
Attributes
tsnr_map
:np.ndarray
- Map of temporal signal to noise ratio.
Initialise a temporal signal to noise ratio (tSNR) class instance.
Parameters
pixel_array
:np.ndarray
- A array containing the signal from each voxel with the last dimension being repeated dynamics i.e. the array needed to generate a tSNR map would have dimensions [x, y, z, d].
affine
:np.ndarray
- A matrix giving the relationship between voxel coordinates and world coordinates.
mask
:np.ndarray
, optional- A boolean mask of the voxels to fit. Should be the shape of the desired tSNR map rather than the raw data i.e. omit the dynamics dimension.
Expand source code
class Tsnr: """ Attributes ---------- tsnr_map : np.ndarray Map of temporal signal to noise ratio. """ def __init__(self, pixel_array, affine, mask=None): """Initialise a temporal signal to noise ratio (tSNR) class instance. Parameters ---------- pixel_array : np.ndarray A array containing the signal from each voxel with the last dimension being repeated dynamics i.e. the array needed to generate a tSNR map would have dimensions [x, y, z, d]. affine : np.ndarray A matrix giving the relationship between voxel coordinates and world coordinates. mask : np.ndarray, optional A boolean mask of the voxels to fit. Should be the shape of the desired tSNR map rather than the raw data i.e. omit the dynamics dimension. """ np.seterr(divide='ignore', invalid='ignore') self.pixel_array = pixel_array self.shape = pixel_array.shape[:-1] self.dimensions = len(pixel_array.shape) self.n_d = pixel_array.shape[-1] self.affine = affine # Generate a mask if there isn't one specified if mask is None: self.mask = np.ones(self.shape, dtype=bool) else: self.mask = mask # Don't process any nan values self.mask[np.isnan(np.sum(pixel_array, axis=-1))] = False self.pixel_array = self.pixel_array * \ np.repeat(self.mask[..., np.newaxis], self.n_d, axis=-1) # Initialise output attributes self.tsnr_map = np.zeros(self.shape) self.tsnr_map = self.__tsnr__() def __tsnr__(self): # Regress out linear and quadratic temporal drifts associated with # hardware using a GLM of the form Y = X * beta + error # as in Hutton C et al. The impact of physiological noise correction # on fMRI at 7T. NeuroImage 2011;57:101–112 doi: # 10.1016/j.neuroimage.2011.04.018. # Vectorise image pixel_array_vector = np.reshape(self.pixel_array, (np.prod(self.shape), self.n_d)) x = np.vstack([np.ones(self.n_d), np.arange(1, self.n_d + 1), np.arange(1, self.n_d + 1) ** 2]).T beta = np.linalg.pinv(x).dot(pixel_array_vector.T) pixel_array_vector_detrended = pixel_array_vector.T - \ x[:, 1:].dot(beta[1:]) pixel_array_detrended = pixel_array_vector_detrended.T.reshape(( *self.shape, self.n_d)) tsnr_map = pixel_array_detrended.mean(axis=-1) / \ pixel_array_detrended.std(axis=-1) # Might want to try # ddof=1 as per Kevins code... tsnr_map[tsnr_map > 1000] = 0 tsnr_map = np.nan_to_num(tsnr_map) return tsnr_map def to_nifti(self, output_directory=os.getcwd(), base_file_name='Output'): """Exports tSNR maps 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'. """ os.makedirs(output_directory, exist_ok=True) base_path = os.path.join(output_directory, base_file_name) tsnr_nifti = nib.Nifti1Image(self.tsnr_map, affine=self.affine) nib.save(tsnr_nifti, base_path + '_tsnr_map.nii.gz')
Methods
def to_nifti(self, output_directory='/home/runner/work/ukat/ukat', base_file_name='Output')
-
Exports tSNR maps 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'.