예제 #1
0
def nfold(im, mod, center=None, mask=None, fill_value=0.0):
    """ 
    Returns an images averaged according to n-fold rotational symmetry. This can be used to
    boost the signal-to-noise ratio on an image with known symmetry, e.g. a diffraction pattern.

    Parameters
    ----------
    im : array_like, ndim 2
        Image to be azimuthally-symmetrized.
    center : array_like, shape (2,) or None, optional
        Coordinates of the center (in pixels). If ``center=None``, the image is rotated around
        its center, i.e. ``center=(rows / 2 - 0.5, cols / 2 - 0.5)``.
    mod : int
        Fold symmetry number. Valid numbers must be a divisor of 360.
    mask : `~numpy.ndarray` or None, optional
        Mask of `image`. The mask should evaluate to `True` (or 1) on invalid pixels. 
        If None (default), no mask is used.
    fill_value : float, optional
        In the case of a mask that overlaps with itself when rotationally averaged,
        the overlapping regions will be filled with this value.

    Returns
    -------
    out : `~numpy.ndarray`, dtype float
        Symmetrized image.

    Raises
    ------
    ValueError : If `mod` is not a divisor of 360 deg.
    """
    if (360 % mod):
        raise ValueError(
            '{}-fold rotational symmetry is not valid (not a divisor of 360).'.
            format(mod))
    angles = range(0, 360, int(360 / mod))

    # Data-type must be float because of use of NaN
    im = np.array(im, dtype=np.float, copy=True)

    if mask is not None:
        im[mask] = np.nan

    kwargs = {
        'center': center,
        'mode': 'constant',
        'cval': 0,
        'preserve_range': True
    }

    # Use weights because edges of the pictures, which might be cropped by the rotation
    # should not count in the average
    wt = np.ones_like(im, dtype=np.uint8)
    weights = (rotate(wt, angle, **kwargs) for angle in angles)
    rotated = (rotate(im, angle, **kwargs) for angle in angles)

    avg = average(rotated, weights=weights, ignore_nan=True)
    return nan_to_num(avg, fill_value, copy=False)
예제 #2
0
    def test_vs_numpy(self):
        """ Test average vs. numpy.average """
        stream = [np.random.random(size=(64, 64)) for _ in range(5)]
        stack = np.dstack(stream)

        for axis in (0, 1, 2, None):
            with self.subTest("axis = {}".format(axis)):
                from_stream = average(stream, axis=axis)
                from_numpy = np.average(stack, axis=axis)
                self.assertTrue(np.allclose(from_numpy, from_stream))
예제 #3
0
    def test_weighted_average(self):
        """ Test results of weighted average against numpy.average """
        stream = [np.random.random(size=(16, 16)) for _ in range(5)]

        with self.subTest("float weights"):
            weights = [random() for _ in stream]
            from_average = average(stream, weights=weights)
            from_numpy = np.average(
                np.dstack(stream), axis=2, weights=np.array(weights)
            )
            self.assertTrue(np.allclose(from_average, from_numpy))

        with self.subTest("array weights"):
            weights = [np.random.random(size=stream[0].shape) for _ in stream]
            from_average = average(stream, weights=weights)
            from_numpy = np.average(
                np.dstack(stream), axis=2, weights=np.dstack(weights)
            )
            self.assertTrue(np.allclose(from_average, from_numpy))
예제 #4
0
def reflection(im, angle, center=None, mask=None, fill_value=0.0):
    """
    Symmetrize an image according to a reflection plane.

    Parameters
    ----------
    im : array_like, ndim 2
        Image to be symmetrized.
    angle : float
        Angle (in degrees) of the line that defines the reflection plane. This angle
        increases counter-clockwise from the positive x-axis. Angles
        larger that 360 are mapped back to [0, 360). Note that ``angle`` and ``angle + 180``
        are equivalent.
    center : array_like, shape (2,) or None, optional
        Coordinates of the center (in pixels). If ``center=None``, the image is rotated around
        its center, i.e. ``center=(rows / 2 - 0.5, cols / 2 - 0.5)``.
    mask : `~numpy.ndarray` or None, optional
        Mask of `image`. The mask should evaluate to `True` (or 1) on valid pixels. 
        If None (default), no mask is used.
    fill_value : float, optional
        In the case of a mask that overlaps with itself when rotationally averaged,
        the overlapping regions will be filled with this value.

    Returns
    -------
    out : `~numpy.ndarray`, dtype float
        Symmetrized image.
    """
    angle = float(angle) % 360

    # Data-type must be float because of use of NaN
    im = np.array(im, dtype=np.float, copy=True)
    reflected = np.array(im, copy=True)  # reflected image

    if mask is not None:
        invalid_pixels = np.logical_not(mask)
        im[invalid_pixels] = np.nan
        reflected[invalid_pixels] = np.nan

    kwargs = {
        "center": center,
        "mode": "constant",
        "cval": 0,
        "preserve_range": True
    }

    # Rotate the 'reflected' image so that the reflection line is the x-axis
    # Flip the image along the y-axis
    # Rotate back to original orientation
    # FIXME: this will not work properly for images that are offcenter
    reflected = rotate(reflected, -angle, **kwargs)
    reflected = mirror(reflected, axes=0)
    reflected = rotate(reflected, angle, **kwargs)

    return nan_to_num(average([im, reflected]), fill_value, copy=False)
예제 #5
0
    def test_ignore_nan(self):
        """ Test that NaNs are handled correctly """
        stream = [np.random.random(size=(16, 12)) for _ in range(5)]
        for s in stream:
            s[randint(0, 15), randint(0, 11)] = np.nan

        with catch_warnings():
            simplefilter("ignore")
            from_average = average(stream, ignore_nan=True)
        from_numpy = np.nanmean(np.dstack(stream), axis=2)
        self.assertTrue(np.allclose(from_average, from_numpy))
예제 #6
0
def _raw_combine(timedelay, raw, exclude_scans, normalize, align, valid_mask,
                 dtype):

    images = raw.itertime(timedelay, exclude_scans=exclude_scans)

    if align:
        # Note : the fast = False fixes issue #11, where single crystal images were not successfully aligned.
        images = ialign(images, mask=valid_mask)

    # Set up normalization
    if normalize:
        images, images2 = itercopy(images, copies=2)

        # Compute the total intensity of first image
        # This will be the reference point
        first2, images2 = peek(images2)
        initial_weight = np.sum(first2[valid_mask])
        weights = (initial_weight / np.sum(image[valid_mask])
                   for image in images2)
    else:
        weights = None

    return average(images, weights=weights).astype(dtype)
예제 #7
0
 def test_trivial(self):
     """ Test average() on a stream of zeroes """
     stream = repeat(np.zeros((64, 64), dtype=np.float), times=5)
     for av in average(stream):
         self.assertTrue(np.allclose(av, np.zeros_like(av)))
예제 #8
0
 def background(self):
     """ Laser background """
     backgrounds = map(diffread,
                       iglob(join(self.source, "background.*.pumpoff.tif")))
     return average(backgrounds)
예제 #9
0
def nfold(im, mod, center=None, mask=None, fill_value=0.0):
    """
    Returns an images averaged according to n-fold rotational symmetry. This can be used to
    boost the signal-to-noise ratio on an image with known symmetry, e.g. a diffraction pattern.

    Parameters
    ----------
    im : array_like, ndim 2
        Image to be azimuthally-symmetrized.
    center : array_like, shape (2,) or None, optional
        Coordinates of the center (in pixels) in the format ``center=[col, row]``. If ``center=None``,
        the image is rotated around the center of the array, i.e. ``center=(cols / 2 - 0.5, rows / 2 - 0.5)``.
    mod : int
        Fold symmetry number. Valid numbers must be a divisor of 360.
    mask : `~numpy.ndarray` or None, optional
        Mask of `image`. The mask should evaluate to `True` (or 1) on valid pixels.
        If None (default), no mask is used.
    fill_value : float, optional
        In the case of a mask that overlaps with itself when rotationally averaged,
        the overlapping regions will be filled with this value.

    Returns
    -------
    out : `~numpy.ndarray`, dtype float
        Symmetrized image.

    Raises
    ------
    ValueError : If `mod` is not a divisor of 360 deg.
    """
    if 360 % mod:
        raise ValueError(
            f"{mod}-fold rotational symmetry is not valid (not a divisor of 360)."
        )
    angles = range(0, 360, int(360 / mod))

    im = np.array(im, copy=True)

    kwargs = {
        "center": center,
        "mode": "constant",
        "cval": 0,
        "preserve_range": True
    }

    if mask is None:
        return ns.average(rotate(im, angle, **kwargs) for angle in angles)

    # Use weights because edges of the pictures, which might be cropped by the rotation
    # should not count in the average
    wt = np.ones_like(mask, dtype=im.dtype)
    wt[np.logical_not(mask)] = 0

    weights = (rotate(wt, angle, **kwargs) for angle in angles)
    imgs = (rotate(im, angle, **kwargs) for angle in angles)

    avg = ns.average(imgs, weights=weights)

    # Mask may overlap with itself during symmetrization. At those points, the average
    # will be zero (because the weights are 0 there)
    # However, because users may want to change that value to `fill_value != 0`, we need
    # to know where is the overlap
    if fill_value != 0.0:
        invalid_pixels = np.logical_not(mask)
        masks = (rotate(invalid_pixels, angle, **kwargs) for angle in angles)
        overlap = ns.prod(masks).astype(bool)  # equivalent to logical_and
        avg[overlap] = fill_value

    return ns.nan_to_num(avg, fill_value=fill_value)