def isnr(images, fill_value=0.0): """ Streaming, pixelwise signal-to-noise ratio (SNR). Parameters ---------- images : iterable of ndarray These images should represent identical measurements. ``images`` can also be a generator. fill_value : float, optional Division-by-zero results will be filled with this value. Yields ------ snr : `~numpy.ndarray` Pixelwise signal-to-noise ratio See Also -------- snr_from_collection : pixelwise signal-to-noise ratio from a collection of measurements """ first, images = peek(images) snr = np.empty_like(first) images1, images2 = itercopy(images, 2) for mean, std in zip(imean(images1), istd(images2)): valid = std != 0 snr[valid] = mean[valid] / std[valid] snr[np.logical_not(valid)] = fill_value yield snr
def mask_from_collection(images, px_thresh=(0, 3e4), std_thresh=None): """ Determine binary mask from a set of images. These images should represent identical measurements, e.g. a set of diffraction patterns before photoexcitation. Pixels are rejected on the following two criteria: * Pixels with a value above a certain threshold or below zero, for any image in the set, are considered dead; * Pixels with a cumulative standard deviation above a certain threshold are considered uncertain. This function operates in constant-memory; it is therefore safe to use on a large collection of images (>10GB). Parameters ---------- images : iterable of ndarray These images should represent identical measurements. ``images`` can also be a generator. px_thresh : float or iterable, optional Pixels with a value outside of [``min(px_thresh)``, ``max(px_thresh)``] in any of the images in ``images`` are rejected. If ``px_thresh`` is a single float, it is assumed to be the maximal intensity, and no lower bound is enforced. std_thresh : int or float or None, optional Standard-deviation threshold. If the standard deviation of a pixel exceeds ``std_thresh``, it is rejected. If None (default), a threshold is not enforced. Returns ------- mask : `~numpy.ndarray`, dtype bool Pixel mask. Pixels where ``mask`` is True are invalid. Notes ----- ``numpy.inf`` can be used to have a lower pixel value bound but no upper bound. For example, to reject all negative pixels only, set ``px_thresh = (0, numpy.inf)``. """ if isinstance(px_thresh, Iterable): min_int, max_int = min(px_thresh), max(px_thresh) else: min_int, max_int = None, px_thresh first, images = peek(images) mask = np.zeros_like(first, dtype=bool) # 0 = False if std_thresh is not None: images, images_for_std = itercopy(images) std_calc = istd(images_for_std) else: std_calc = repeat(np.inf) for image, std in zip(images, std_calc): mask[image > max_int] = True if std_thresh is not None: mask[std > std_thresh] = True if min_int is not None: mask[image < min_int] = True return mask
def test_against_numpy_std(self): stream = [np.random.random((16, 7, 3)) for _ in range(10)] stack = np.stack(stream, axis=-1) with catch_warnings(): simplefilter("ignore") for axis in (0, 1, 2, None): for ddof in range(4): with self.subTest("axis = {}, ddof = {}".format(axis, ddof)): from_numpy = np.std(stack, axis=axis, ddof=ddof) from_ivar = last(istd(stream, axis=axis, ddof=ddof)) self.assertSequenceEqual(from_numpy.shape, from_ivar.shape) self.assertTrue(np.allclose(from_ivar, from_numpy))
def test_against_numpy_nanstd(self): source = [np.random.random((16, 12, 5)) for _ in range(10)] for arr in source: arr[randint(0, 15), randint(0, 11), randint(0, 4)] = np.nan stack = np.stack(source, axis=-1) for axis in (0, 1, 2, None): for ddof in range(4): with self.subTest("axis = {}, ddof = {}".format(axis, ddof)): from_numpy = np.nanstd(stack, axis=axis, ddof=ddof) from_ivar = last( istd(source, axis=axis, ddof=ddof, ignore_nan=True) ) self.assertSequenceEqual(from_numpy.shape, from_ivar.shape) self.assertTrue(np.allclose(from_ivar, from_numpy))