def test_chunk_bytes_indirectly(self): s = EBSD(np.zeros((10, 10, 8, 8))) array_out0 = get_dask_array(s) array_out1 = get_dask_array(s, chunk_bytes="25KiB") array_out2 = get_dask_array(s, chunk_bytes=25e3) assert array_out0.chunksize != array_out1.chunksize assert array_out1.chunksize == array_out2.chunksize
def test_get_dask_array(self): s = EBSD((255 * np.random.rand(10, 10, 120, 120)).astype(np.uint8)) dask_array = get_dask_array(s, chunk_shape=8) assert dask_array.chunksize == (8, 8, 120, 120) # Make data lazy s.data = dask_array.rechunk((5, 5, 120, 120)) dask_array = get_dask_array(s) assert dask_array.chunksize == (5, 5, 120, 120)
def test_get_dynamic_background_frequency(self, dummy_signal, std, answer): dtype_out = answer.dtype dask_array = get_dask_array(dummy_signal, dtype=np.float32) kwargs = {} ( kwargs["fft_shape"], kwargs["window_shape"], kwargs["transfer_function"], kwargs["offset_before_fft"], kwargs["offset_after_ifft"], ) = _dynamic_background_frequency_space_setup( pattern_shape=dummy_signal.axes_manager.signal_shape[::-1], std=std, truncate=4.0, ) background = dask_array.map_blocks( func=chunk.get_dynamic_background, filter_func=_fft_filter, dtype_out=dtype_out, dtype=dtype_out, **kwargs, ) # Check for correct data type and gives expected output intensities assert background.dtype == dtype_out assert np.allclose(background[0, 0].compute(), answer, atol=1e-4)
def test_rescale_intensity(self, dummy_signal, dtype_out, answer): dask_array = get_dask_array(dummy_signal, dtype=np.float32) rescaled_patterns = dask_array.map_blocks(func=chunk.rescale_intensity, dtype_out=dtype_out, dtype=dtype_out) assert isinstance(rescaled_patterns, da.Array) assert rescaled_patterns.dtype == dtype_out assert np.allclose(rescaled_patterns[0, 0].compute(), answer, atol=1e-4)
def test_adaptive_histogram_equalization_chunk(self, dummy_signal): dask_array = get_dask_array(dummy_signal) dtype_out = dask_array.dtype kernel_size = (10, 10) nbins = 128 equalized_patterns = dask_array.map_blocks( func=chunk.adaptive_histogram_equalization, kernel_size=kernel_size, nbins=nbins, ) # Check for correct data type and gives expected output intensities assert equalized_patterns.dtype == dtype_out assert np.allclose(equalized_patterns[0, 0].compute(), ADAPT_EQ_UINT8)
def test_rescale_intensity_in_range(self, dummy_signal, in_range, answer): dtype_out = dummy_signal.data.dtype dask_array = get_dask_array(dummy_signal, dtype=np.float32) rescaled_patterns = dask_array.map_blocks( func=chunk.rescale_intensity, in_range=in_range, dtype_out=dtype_out, dtype=dtype_out, ) assert isinstance(rescaled_patterns, da.Array) assert rescaled_patterns.dtype == dtype_out assert np.allclose(rescaled_patterns[0, 0].compute(), answer)
def test_get_dynamic_background_spatial(self, dummy_signal, std, answer): filter_func = gaussian_filter kwargs = {"sigma": std} dtype_out = np.uint8 dask_array = get_dask_array(dummy_signal, dtype=np.float32) background = dask_array.map_blocks( func=chunk.get_dynamic_background, filter_func=filter_func, dtype_out=dtype_out, dtype=dtype_out, **kwargs, ) assert background.dtype == dtype_out assert np.allclose(background[0, 0].compute(), answer)
def test_remove_dynamic_background_spatial_uint16(self, dummy_signal): dtype_out = np.uint16 dask_array = get_dask_array(dummy_signal, dtype=np.float32) corrected_patterns = dask_array.map_blocks( func=chunk.remove_dynamic_background, filter_func=gaussian_filter, operation_func=np.subtract, dtype_out=dtype_out, dtype=dtype_out, sigma=2, ) # Check for correct data type and gives expected output intensities assert corrected_patterns.dtype == dtype_out assert np.allclose(corrected_patterns[0, 0].compute(), DYN_CORR_UINT16_SPATIAL_STD2)
def test_remove_static_background_chunk_scalebg(self, dummy_signal, dummy_background): dtype_out = dummy_signal.data.dtype.type dtype_proc = np.float32 dask_array = get_dask_array(dummy_signal, dtype=dtype_proc) corrected_patterns = dask_array.map_blocks( func=chunk.remove_static_background, static_bg=dummy_background.astype(dtype_proc), operation_func=np.subtract, dtype_out=dtype_out, scale_bg=True, dtype=dtype_out, ) assert corrected_patterns.dtype == dtype_out assert np.allclose(corrected_patterns[0, 0].compute(), STATIC_SUB_SCALEBG_UINT8)
def test_remove_static_background_chunk(self, dummy_signal, dummy_background, operation_func, answer): dtype_out = dummy_signal.data.dtype.type dtype_proc = np.float32 dask_array = get_dask_array(dummy_signal, dtype=dtype_proc) corrected_patterns = dask_array.map_blocks( func=chunk.remove_static_background, static_bg=dummy_background.astype(dtype_proc), operation_func=operation_func, dtype_out=dtype_out, dtype=dtype_out, ) # Check for correct data type and gives expected output intensities assert corrected_patterns.dtype == dtype_out assert isinstance(corrected_patterns, da.Array) assert np.allclose(corrected_patterns[0, 0].compute(), answer)
def test_rescale_intensity_percentiles(self, dummy_signal, percentiles, answer): dtype_out = dummy_signal.data.dtype dask_array = get_dask_array(dummy_signal, dtype=np.float32) rescaled_patterns = dask_array.map_blocks( func=chunk.rescale_intensity, percentiles=percentiles, dtype_out=dtype_out, dtype=dtype_out, ) p1 = rescaled_patterns[0, 0].compute() p2 = rescaled_patterns[0, 1].compute() assert isinstance(rescaled_patterns, da.Array) assert rescaled_patterns.dtype == dtype_out assert np.allclose(p1, answer) assert not np.allclose(p1, p2, atol=1)
def test_remove_dynamic_background_spatial(self, dummy_signal, std, answer): dtype_out = dummy_signal.data.dtype.type dask_array = get_dask_array(dummy_signal, dtype=np.float32) kwargs = {"sigma": std} corrected_patterns = dask_array.map_blocks( func=chunk.remove_dynamic_background, filter_func=gaussian_filter, operation_func=np.subtract, dtype_out=dtype_out, dtype=dtype_out, **kwargs, ) # Check for correct data type and gives expected output intensities assert corrected_patterns.dtype == dtype_out assert np.allclose(corrected_patterns[0, 0].compute(), answer)
def test_average_neighbour_patterns_chunk(self, dummy_signal, dtype_in): w = Window() # Get array to operate on dask_array = get_dask_array(dummy_signal) dtype_out = dask_array.dtype # Get sum of window data for each image nav_shape = dummy_signal.axes_manager.navigation_shape w_sums = convolve( input=np.ones(nav_shape[::-1], dtype=int), weights=w.data, mode="constant", cval=0, ) # Add signal dimensions to arrays to enable their use with # Dask's map_blocks() sig_dim = dummy_signal.axes_manager.signal_dimension nav_dim = dummy_signal.axes_manager.navigation_dimension for _ in range(sig_dim): w_sums = np.expand_dims(w_sums, axis=w_sums.ndim) w = np.expand_dims(w, axis=w.ndim) w_sums = da.from_array(w_sums, chunks=dask_array.chunks[:nav_dim] + (1, ) * sig_dim) averaged_patterns = dask_array.map_blocks( func=chunk.average_neighbour_patterns, window_sums=w_sums, window=w, dtype_out=dtype_in, dtype=dtype_out, ) answer = np.array([255, 109, 218, 218, 36, 236, 255, 36, 0], dtype=np.uint8).reshape((3, 3)) # Check for correct data type and gives expected output intensities assert averaged_patterns.dtype == dtype_out assert np.allclose(averaged_patterns[0, 0].compute(), answer)
def test_remove_dynamic_background_out_range(self, dummy_signal, omax, answer): dtype_out = dummy_signal.data.dtype.type out_range = (0, omax) dask_array = get_dask_array(dummy_signal, dtype=np.float32) corrected_patterns = dask_array.map_blocks( func=chunk.remove_dynamic_background, filter_func=gaussian_filter, operation_func=np.subtract, sigma=2, out_range=out_range, dtype_out=dtype_out, dtype=dtype_out, ) assert corrected_patterns.dtype == dtype_out assert corrected_patterns.max().compute() == omax assert np.allclose(corrected_patterns[0, 0].compute(), answer)
def rescale_intensity( self, relative: bool = False, in_range: Union[None, Tuple[int, int], Tuple[float, float]] = None, out_range: Union[None, Tuple[int, int], Tuple[float, float]] = None, dtype_out: Union[ None, np.dtype, Tuple[int, int], Tuple[float, float] ] = None, percentiles: Union[None, Tuple[int, int], Tuple[float, float]] = None, ): """Rescale image intensities inplace. Output min./max. intensity is determined from `out_range` or the data type range of the :class:`numpy.dtype` passed to `dtype_out` if `out_range` is None. This method is based on :func:`skimage.exposure.rescale_intensity`. Parameters ---------- relative Whether to keep relative intensities between images (default is False). If True, `in_range` must be None, because `in_range` is in this case set to the global min./max. intensity. in_range Min./max. intensity of input images. If None (default), `in_range` is set to pattern min./max intensity. Contrast stretching is performed when `in_range` is set to a narrower intensity range than the input patterns. Must be None if `relative` is True or `percentiles` are passed. out_range Min./max. intensity of output images. If None (default), `out_range` is set to `dtype_out` min./max according to `skimage.util.dtype.dtype_range`. dtype_out Data type of rescaled images, default is input images' data type. percentiles Disregard intensities outside these percentiles. Calculated per image. Must be None if `in_range` or `relative` is passed. Default is None. See Also -------- kikuchipy.pattern.rescale_intensity, :func:`skimage.exposure.rescale_intensity` Examples -------- Image intensities are stretched to fill the available grey levels in the input images' data type range or any :class:`numpy.dtype` range passed to `dtype_out`, either keeping relative intensities between images or not: >>> print(s.data.dtype_out, s.data.min(), s.data.max(), ... s.inav[0, 0].data.min(), s.inav[0, 0].data.max()) uint8 20 254 24 233 >>> s2 = s.deepcopy() >>> s.rescale_intensity(dtype_out=np.uint16) >>> print(s.data.dtype_out, s.data.min(), s.data.max(), ... s.inav[0, 0].data.min(), s.inav[0, 0].data.max()) uint16 0 65535 0 65535 >>> s2.rescale_intensity(relative=True) >>> print(s2.data.dtype_out, s2.data.min(), s2.data.max(), ... s2.inav[0, 0].data.min(), s2.inav[0, 0].data.max()) uint8 0 255 4 232 Contrast stretching can be performed by passing percentiles: >>> s.rescale_intensity(percentiles=(1, 99)) Here, the darkest and brightest pixels within the 1% percentile are set to the ends of the data type range, e.g. 0 and 255 respectively for images of ``uint8`` data type. Notes ----- Rescaling RGB images is not possible. Use RGB channel normalization when creating the image instead. """ if self.data.dtype in rgb_dtypes.values(): raise NotImplementedError( "Use RGB channel normalization when creating the image instead." ) # Determine min./max. intensity of input image to rescale to if in_range is not None and percentiles is not None: raise ValueError( "'percentiles' must be None if 'in_range' is not None." ) elif relative is True and in_range is not None: raise ValueError("'in_range' must be None if 'relative' is True.") elif relative: # Scale relative to min./max. intensity in images in_range = (self.data.min(), self.data.max()) if dtype_out is None: dtype_out = self.data.dtype.type if out_range is None: dtype_out_pass = dtype_out if isinstance(dtype_out, np.dtype): dtype_out_pass = dtype_out.type out_range = dtype_range[dtype_out_pass] # Create dask array of signal images and do processing on this dask_array = get_dask_array(signal=self) # Rescale images rescaled_images = dask_array.map_blocks( func=chunk.rescale_intensity, in_range=in_range, out_range=out_range, dtype_out=dtype_out, percentiles=percentiles, dtype=dtype_out, ) # Overwrite signal images if not self._lazy: with ProgressBar(): if self.data.dtype != rescaled_images.dtype: self.change_dtype(dtype_out) print("Rescaling the image intensities:", file=sys.stdout) rescaled_images.store(self.data, compute=True) else: self.data = rescaled_images
def normalize_intensity( self, num_std: int = 1, divide_by_square_root: bool = False, dtype_out: Optional[np.dtype] = None, ): """Normalize image intensities in inplace to a mean of zero with a given standard deviation. Parameters ---------- num_std Number of standard deviations of the output intensities. Default is 1. divide_by_square_root Whether to divide output intensities by the square root of the signal dimension size. Default is False. dtype_out Data type of normalized images. If None (default), the input images' data type is used. Notes ----- Data type should always be changed to floating point, e.g. ``np.float32`` with :meth:`~hyperspy.signal.BaseSignal.change_dtype`, before normalizing the intensities. Examples -------- >>> np.mean(s.data) 146.0670987654321 >>> s.change_dtype(np.float32) # Or passing dtype_out=np.float32 >>> s.normalize_intensity() >>> np.mean(s.data) 2.6373216e-08 Notes ----- Rescaling RGB images is not possible. Use RGB channel normalization when creating the image instead. """ if self.data.dtype in rgb_dtypes.values(): raise NotImplementedError( "Use RGB channel normalization when creating the image instead." ) if dtype_out is None: dtype_out = self.data.dtype dask_array = get_dask_array(self, dtype=np.float32) normalized_images = dask_array.map_blocks( func=chunk.normalize_intensity, num_std=num_std, divide_by_square_root=divide_by_square_root, dtype_out=dtype_out, dtype=dtype_out, ) # Change data type if requested if dtype_out != self.data.dtype: self.change_dtype(dtype_out) # Overwrite signal patterns if not self._lazy: with ProgressBar(): print("Normalizing the image intensities:", file=sys.stdout) normalized_images.store(self.data, compute=True) else: self.data = normalized_images