Source code for ukat.utils.tools

"""
This module contains multiple auxiliary functions that might be used by
multiple algorithms

"""
import numpy as np
from scipy.ndimage import zoom
from skimage.restoration import unwrap_phase


[docs]def unwrap_phase_image(pixel_array, wrap_around=False): """ From an image wrapped to lie in the interval [-pi, pi], this function recovers the original, unwrapped image. Read references in https://scikit-image.org/docs/dev/auto_examples/filters/plot_phase_unwrap.html Parameters ---------- pixel_array : np.ndarray A 2D/3D array containing the phase image. wrap_around : boolean Optional input argument. By default, this script does not apply the scipy wrap_around in the input image. Returns ------- np.ndarray with the phase of pixel_array unwrapped. """ wrapped_phase = np.angle(np.exp(2j * pixel_array)) return unwrap_phase(wrapped_phase, wrap_around=wrap_around)
[docs]def convert_to_pi_range(pixel_array): """ Rescale the image values to the interval [-pi, pi]. Parameters ---------- pixel_array : np.ndarray Returns ------- radians_array : np.ndarray An array containing with the same shape as pixel_array scaled to the range [-pi, pi]. """ if (np.amax(pixel_array) > 3.2) or (np.amin(pixel_array) < -3.2): # The value 3.2 was chosen instead of np.pi in order # to give some margin. pi_array = np.pi * np.ones(np.shape(pixel_array)) min_array = np.amin(pixel_array) * np.ones(np.shape(pixel_array)) max_array = np.amax(pixel_array) * np.ones(np.shape(pixel_array)) radians_array = (2.0 * pi_array * (pixel_array - min_array) / (max_array - min_array)) - pi_array else: # It means it's already on the interval [-pi, pi] radians_array = pixel_array return radians_array
[docs]def resize_array(pixel_array, factor=1, target_size=None): """ Resizes the given pixel_array, using target_size as the reference of the resizing operation (if None, then there's no resizing). This method applies a resize_factor to the first 2 axes of the input array. The remaining axes are left unchanged. Example 1: (10, 10, 2) => (5, 5, 2) with resize_factor = 0.5 Example 2: (10, 10, 10, 2) => (20, 20, 10, 2) with resize_factor = 2 Parameters ---------- pixel_array : np.ndarray factor : boolean Optional input argument. This is the resize factor defined by the user and it is applied in the scipy.ndimage.zoom target_size : boolean Optional input argument. By default, this script does not apply the scipy wrap_around in the input image. Returns ------- resized_array : np.ndarray where the size of the first 2 dimensions is np.shape(pixel_array) * factor. The remaining dimensions (or axes) will have a the same size as in pixel_array. """ if target_size is not None: factor = target_size / np.shape(pixel_array)[0] resize_factor = np.ones(len(np.shape(pixel_array))) resize_factor[0] = factor resize_factor[1] = factor resized_array = zoom(pixel_array, resize_factor) return resized_array
[docs]def mask_slices(shape, slices, mask=None): """ Get mask to limit processing to specific slices. This function allows to quickly get a mask of Trues of the right shape to limit processing to specific slices. If `mask` is provided it outputs a new mask corresponding to the input mask but only on the specified slices. Parameters ---------- shape : tuple shape of mask to be created slices : int or list (of ints) slice indices where mask is to be True mask : np.ndarray (of booleans) original mask, if provided this function will return a mask of Falses in all elements except at the locations of the True elements of this `mask` in the slices given by `slices` Returns ------- np.ndarray (of booleans) """ # Input type checks if not isinstance(shape, tuple): raise ValueError("`shape` must be a tuple") if not isinstance(slices, (int, list)): raise ValueError("`slices` must be an integer or a list of integers") # Check elements of `slices` are ints if (isinstance(slices, list) and not all(isinstance(x, int) for x in slices)): raise ValueError("Every element of `slices` must be an integer") # Check elements of `slices` within range defined by `shape` if isinstance(slices, int): s_min = slices s_max = slices elif isinstance(slices, list): s_min = min(slices) s_max = max(slices) if not(s_min >= 0 and s_max+1 <= shape[2]): msg = f"The elements of `slices` must be > 0 and <= {shape[2]-1}" raise ValueError(msg) # Ensure shape and the dimensions of mask match if mask is not None: assert isinstance(mask, np.ndarray), "`mask` must be a numpy" assert mask.dtype == "bool", "The elements of `mask` must be bool" assert (shape == mask.shape), "The shape of `mask` must match `shape`" # Make mask of Trues at the specified slices template = np.full(shape, False) template[:, :, slices] = True # If `mask` provided, apply it to the template if mask is None: final_mask = template else: final_mask = np.logical_and(mask, template) return final_mask
[docs]def image_stats(pixel_array): """ This functions takes an image and calculates its mean, std, min and max. It is used in the unit tests, but it can also be used for other image processing tasks. Parameters ---------- pixel_array : np.ndarray Returns ------- list() with 4 values calculated from pixel_array: [mean, standard deviation, minimum, maximum] """ mean = np.nanmean(pixel_array) std = np.nanstd(pixel_array) minimum = np.nanmin(pixel_array) maximum = np.nanmax(pixel_array) return [mean, std, minimum, maximum]