Ejemplo n.º 1
0
def test_cross_correlate_masked_output_shape():
    """Masked normalized cross-correlation should return a shape
    of N + M + 1 for each transform axis."""
    shape1 = (15, 4, 5)
    shape2 = (6, 12, 7)
    expected_full_shape = tuple(np.array(shape1) + np.array(shape2) - 1)
    expected_same_shape = shape1

    arr1 = np.zeros(shape1)
    arr2 = np.zeros(shape2)
    # Trivial masks
    m1 = np.ones_like(arr1)
    m2 = np.ones_like(arr2)

    full_xcorr = cross_correlate_masked(arr1,
                                        arr2,
                                        m1,
                                        m2,
                                        axes=(0, 1, 2),
                                        mode='full')
    assert_equal(full_xcorr.shape, expected_full_shape)

    same_xcorr = cross_correlate_masked(arr1,
                                        arr2,
                                        m1,
                                        m2,
                                        axes=(0, 1, 2),
                                        mode='same')
    assert_equal(same_xcorr.shape, expected_same_shape)
Ejemplo n.º 2
0
def test_cross_correlate_masked_over_axes():
    """Masked normalized cross-correlation over axes should be
    equivalent to a loop over non-transform axes."""
    # See random number generator for reproducible results
    np.random.seed(23)

    arr1 = np.random.random((8, 8, 5))
    arr2 = np.random.random((8, 8, 5))

    m1 = np.random.choice([True, False], arr1.shape)
    m2 = np.random.choice([True, False], arr2.shape)

    # Loop over last axis
    with_loop = np.empty_like(arr1, dtype=np.complex)
    for index in range(arr1.shape[-1]):
        with_loop[:, :, index] = cross_correlate_masked(arr1[:, :, index],
                                                        arr2[:, :, index],
                                                        m1[:, :, index],
                                                        m2[:, :, index],
                                                        axes=(0, 1),
                                                        mode='same')

    over_axes = cross_correlate_masked(arr1,
                                       arr2,
                                       m1,
                                       m2,
                                       axes=(0, 1),
                                       mode='same')

    testing.assert_array_almost_equal(with_loop, over_axes)
Ejemplo n.º 3
0
def test_cross_correlate_masked_test_against_mismatched_dimensions():
    """Masked normalized cross-correlation should raise an error if array
    dimensions along non-transformation axes are mismatched."""
    shape1 = (23, 1, 1)
    shape2 = (6, 2, 2)

    arr1 = np.zeros(shape1)
    arr2 = np.zeros(shape2)

    # Trivial masks
    m1 = np.ones_like(arr1)
    m2 = np.ones_like(arr2)

    with testing.raises(ValueError):
        cross_correlate_masked(arr1, arr2, m1, m2, axes=(1, 2))
Ejemplo n.º 4
0
def test_cross_correlate_masked_side_effects():
    """Masked normalized cross-correlation should not modify the inputs."""
    shape1 = (2, 2, 2)
    shape2 = (2, 2, 2)

    arr1 = np.zeros(shape1)
    arr2 = np.zeros(shape2)

    # Trivial masks
    m1 = np.ones_like(arr1)
    m2 = np.ones_like(arr2)

    for arr in (arr1, arr2, m1, m2):
        arr.setflags(write=False)

    cross_correlate_masked(arr1, arr2, m1, m2)
Ejemplo n.º 5
0
def test_cross_correlate_masked_output_range():
    """Masked normalized cross-correlation should return between 1 and -1."""
    # See random number generator for reproducible results
    np.random.seed(23)

    # Array dimensions must match along non-transformation axes, in
    # this case
    # axis 0
    shape1 = (15, 4, 5)
    shape2 = (15, 12, 7)

    # Initial array ranges between -5 and 5
    arr1 = 10 * np.random.random(shape1) - 5
    arr2 = 10 * np.random.random(shape2) - 5

    # random masks
    m1 = np.random.choice([True, False], arr1.shape)
    m2 = np.random.choice([True, False], arr2.shape)

    xcorr = cross_correlate_masked(arr1, arr2, m1, m2, axes=(1, 2))

    # No assert array less or equal, so we add an eps
    # Also could not find an `assert_array_greater`, Use (-xcorr) instead
    eps = np.finfo(np.float).eps
    testing.assert_array_less(xcorr, 1 + eps)
    testing.assert_array_less(-xcorr, 1 + eps)
Ejemplo n.º 6
0
def test_cross_correlate_masked_autocorrelation_trivial_masks():
    """Masked normalized cross-correlation between identical arrays
    should reduce to an autocorrelation even with random masks."""
    # See random number generator for reproducible results
    np.random.seed(23)

    arr1 = camera()

    # Random masks with 75% of pixels being valid
    m1 = np.random.choice([True, False], arr1.shape, p=[3 / 4, 1 / 4])
    m2 = np.random.choice([True, False], arr1.shape, p=[3 / 4, 1 / 4])

    xcorr = cross_correlate_masked(arr1,
                                   arr1,
                                   m1,
                                   m2,
                                   axes=(0, 1),
                                   mode='same',
                                   overlap_ratio=0).real
    max_index = np.unravel_index(np.argmax(xcorr), xcorr.shape)

    # Autocorrelation should have maximum in center of array
    # uint8 inputs will be processed in float32, so reduce decimal to 5
    assert_almost_equal(xcorr.max(), 1, decimal=5)
    assert_array_equal(max_index, np.array(arr1.shape) / 2)
Ejemplo n.º 7
0
def calculate_offsets(previous, current, mask, dt):
    """Calculate offsets between two frames"""
    cross_corr = cross_correlate_masked(current, previous, mask, mask, axes=(0, 1), overlap_ratio=OVERLAP_RATIO)

    maxima = np.stack(np.nonzero(cross_corr == cross_corr.max()), axis=1)
    center = np.mean(maxima, axis=0)
    shift = center - np.array(previous.shape) + 1

    dy, dx = shift * PLATESCALE / dt
    dr = np.hypot(dx, dy)
    theta = np.mod(np.rad2deg(np.arctan2(dy, dx)), 360)
    offsets = np.array([dx, dy, dr, theta])
    xy_err = 0.5 * PLATESCALE / dt
    r_err = np.sqrt(2) * xy_err
    theta_err = np.rad2deg(xy_err / r_err)
    errors = np.array([xy_err, xy_err, r_err, theta_err])
    return np.real(cross_corr), offsets, errors
Ejemplo n.º 8
0
    # gauss_highpass = coadded_cube - lowpass
    # Y = np.median(coadded_cube, axis=0)

    coadded_cube  #-= Y

    logger.debug(f"size after coadding: {coadded_cube.shape}")

    mask = get_mask()

    correllelagrams = []
    shifts = []
    for i in tqdm.trange(coadded_cube.shape[0] - 1):
        previous, current = coadded_cube[i], coadded_cube[i + 1]
        cross_corr = cross_correlate_masked(current,
                                            previous,
                                            mask,
                                            mask,
                                            axes=(0, 1),
                                            overlap_ratio=OVERLAP_RATIO)
        correllelagrams.append(cross_corr)

        maxima = np.stack(np.nonzero(cross_corr == cross_corr.max()), axis=1)
        center = np.mean(maxima, axis=0)
        shift = center - np.array(previous.shape) + 1

        dy, dx = -shift
        dr = np.sqrt(dx**2 + dy**2)
        dt = np.arctan2(dy, dx)
        logger.debug(f"dx: {dx:.01f} px\tdy: {dy:.01f} px\tdr: {dr:.01f} px")
        speedscale = PLATESCALE * args.rate / args.N
        logger.debug(
            f"{dx * speedscale:.01f} m/s\t{dy * speedscale:.01f} m/s\t{dr * speedscale:.01f} m/s"
Ejemplo n.º 9
0
def bragg_peaks(im, mask=None, center=None, min_dist=None):
    """
    Extract the position of Bragg peaks in a single-crystal diffraction pattern.

    .. versionadded:: 2.1.3

    Parameters
    ----------
    im : ndarray, shape (N,M)
        Single-crystal diffraction pattern.
    mask : ndarray, shape (N,M), dtype bool, optional
        Mask that evaluates to `True` on valid pixels of ``im``.
    center : 2-tuple, optional
        Center of the diffraction pattern, in ``(row, col)`` format.
        If ``None``, the center will be determined via :func:`autocenter`.
    min_dist : float or None, optional
        Minimum distance between Bragg peaks (in pixel coordinates). Peaks that are closer
        than this distance will be considered the same peak, and only one of them will
        be returned. If `None` (default), the minimum distance is guessed based on the
        image size.

    Returns
    -------
    peaks : list of 2-tuples
        List of coordinates ``[row, col]`` for every detected peak, sorted
        in order of how close they are to the center of the image.

    References
    ----------
    Liu, Lai Chung. Chemistry in Action: Making Molecular Movies with Ultrafast
    Electron Diffraction and Data Science, Chapter 2. Springer Nature, 2020.
    """
    if center is None:
        center = autocenter(im=im, mask=mask)

    im = np.array(im, copy=True, dtype=float)
    im -= im.min()

    with catch_warnings():
        simplefilter("ignore", category=RuntimeWarning)
        im /= gaussian_filter(input=im, sigma=min(im.shape) / 20, truncate=2)
    im = np.nan_to_num(im, copy=False)

    autocorr = np.abs(
        cross_correlate_masked(arr1=im, arr2=im, m1=mask, m2=mask,
                               mode="same"))

    # The regions of interest are defined on the labels made
    # from the autocorrelation of the image. The center of the autocorr
    # is the center of the array; we need to correct the offset.
    # This also allows to use the mask on properties derived
    # from the autocorr
    autocorr = shift(
        autocorr,
        shift=np.asarray(center) - np.array(im.shape) / 2,
        order=1,
        mode="nearest",
    )

    laplacian = -1 * laplace(autocorr)
    regions = (laplacian > 0.02) * mask

    # To prevent noise from looking like actual peaks,
    # we erode labels using a small selection area
    regions = binary_erosion(regions, selem=disk(2))

    labels = label(regions, return_num=False)
    props = regionprops(label_image=labels, intensity_image=im)
    candidates = [
        prop for prop in props if not np.any(np.isnan(prop.weighted_centroid))
    ]

    # Some regions are very close to each other; we prune them!
    if min_dist is None:
        min_dist = min(im.shape) / 100

    peaks = list()
    for prop in candidates:
        pos = np.asarray(prop.weighted_centroid)
        if any((np.linalg.norm(peak - pos) < min_dist) for peak in peaks):
            continue
        else:
            peaks.append(pos)

    return sorted(peaks, key=lambda p: np.linalg.norm(p - center))