コード例 #1
0
ファイル: _shape_base.py プロジェクト: toslunar/cupy
def apply_along_axis(func1d, axis, arr, *args, **kwargs):
    """Apply a function to 1-D slices along the given axis.

    Args:
        func1d (function (M,) -> (Nj...)): This function should accept 1-D
            arrays. It is applied to 1-D slices of ``arr`` along the specified
            axis. It must return a 1-D ``cupy.ndarray``.
        axis (integer): Axis along which ``arr`` is sliced.
        arr (cupy.ndarray (Ni..., M, Nk...)): Input array.
        args: Additional arguments for ``func1d``.
        kwargs: Additional keyword arguments for ``func1d``.

    Returns:
        cupy.ndarray: The output array. The shape of ``out`` is identical to
        the shape of ``arr``, except along the ``axis`` dimension. This
        axis is removed, and replaced with new dimensions equal to the
        shape of the return value of ``func1d``. So if ``func1d`` returns a
        scalar ``out`` will have one fewer dimensions than ``arr``.

    .. seealso:: :func:`numpy.apply_along_axis`
    """
    ndim = arr.ndim
    axis = internal._normalize_axis_index(axis, ndim)
    inarr_view = cupy.moveaxis(arr, axis, -1)

    # compute indices for the iteration axes, and append a trailing ellipsis to
    # prevent 0d arrays decaying to scalars
    inds = index_tricks.ndindex(inarr_view.shape[:-1])
    inds = (ind + (Ellipsis,) for ind in inds)

    # invoke the function on the first item
    try:
        ind0 = next(inds)
    except StopIteration:
        raise ValueError(
            'Cannot apply_along_axis when any iteration dimensions are 0'
        )
    res = func1d(inarr_view[ind0], *args, **kwargs)
    if cupy.isscalar(res):
        # scalar outputs need to be transfered to a device ndarray
        res = cupy.asarray(res)

    # build a buffer for storing evaluations of func1d.
    # remove the requested axis, and add the new ones on the end.
    # laid out so that each write is contiguous.
    # for a tuple index inds, buff[inds] = func1d(inarr_view[inds])
    buff = cupy.empty(inarr_view.shape[:-1] + res.shape, res.dtype)

    # save the first result, then compute and save all remaining results
    buff[ind0] = res
    for ind in inds:
        buff[ind] = func1d(inarr_view[ind], *args, **kwargs)

    # restore the inserted axes back to where they belong
    for i in range(res.ndim):
        buff = cupy.moveaxis(buff, -1, axis)

    return buff
コード例 #2
0
def logneg(wave, n, partition):

    L, la, lb, lc1, lc2 = int(partition[0]), int(partition[1]), int(
        partition[2]), int(partition[3]), int(partition[4])

    # region A
    ps = cp.reshape(wave, (2**lc1, 2**la, 2**lc2, 2**lb))
    ps = cp.moveaxis(ps, 0, 1)
    ps = cp.reshape(ps, (2**la, 2**(L - la)))
    # entanglement entropy in region A
    en = ent(ps, n, L, la)
    # sa and sar stand for von-Neumann and Renyi entanglement entropies
    sa, sar = en[0], en[1]

    # region B
    ps = cp.reshape(wave, (2**(L - lb), 2**lb))
    en = ent(ps, n, L, L - lb)
    sb, sbr = en[0], en[1]

    # region C
    # since C composed of c1 and c2, we need to re-arrange the index to combine c1 and c2 into
    # a connected region
    ps = cp.reshape(wave, (2**lc1, 2**la, 2**lc2, 2**lb))
    ps = cp.moveaxis(ps, 1, 2)
    ps = cp.reshape(ps, (2**(lc1 + lc2), 2**(la + lb)))
    en = ent(ps, n, L, lc1 + lc2)
    sc, scr = en[0], en[1]

    # log(negativity)
    rab = cp.dot(ps.T, cp.conj(ps))  #reduced density matrix by tracing out C
    # reshape the reduced density matrix to have 4 indices to facilitate partial transpose
    rab = cp.reshape(rab, (2**la, 2**lb, 2**la, 2**lb))

    # partial transpose on A
    pab = cp.moveaxis(rab, 0, 2)
    # rearrange indices to make pab into a matrix
    pab = pab.reshape(2**(la + lb), 2**(la + lb))
    # SVD of partial transposed density matrix
    sp = cp.linalg.svd(pab, compute_uv=False)
    # definition of logarithmic negativity
    logn = cp.log2(cp.sum(sp))
    tol = 1e-10
    # returns logarithmic negativity and two mutual information
    result = np.array([logn, sa + sb - sc, sar + sbr - scr])
    # chop small values to be zero
    result[abs(result) < tol] = 0.0

    return result
コード例 #3
0
def evo(steps, wave, prob, L, n, partition):
    von = cp.zeros(steps, dtype='float64')  # von-Neumann entropy
    renyi = cp.zeros(steps, dtype='float64')  # Renyi entropy
    neg = cp.zeros(steps, dtype='float64')  # logarithmic negativity
    mut = cp.zeros(
        steps, dtype='float64')  # mutual information using von-Neumann entropy
    mutr = cp.zeros(
        steps, dtype='float64')  # mutual information in terms of Renyi entropy

    for t in range(steps):
        # evolve over odd links
        for i in range(L // 2):
            wave = unitary(wave, i, L)

        # measurement layer
        for i in range(L):
            wave = measure(wave, prob, i, L)

        # before evolve on even link, we need to rearrange indices first to accommodate the boundary condition PBC
        wave = cp.reshape(wave, (2, 2**(L - 2), 2))
        # move the last site into the first one such that the unitaries can connect the 1st and the last site
        wave = cp.moveaxis(wave, -1, 0)
        wave = wave.flatten()

        # evolve over even links
        for i in range(L // 2):
            wave = unitary(wave, i, L)

        #shift the index back to the original order after evolution
        wave = cp.reshape(wave, (2, 2, 2**(L - 2)))
        wave = cp.moveaxis(wave, -1, 0)
        wave = cp.moveaxis(wave, -1, 0).flatten()

        #measurement layer
        for i in range(L):
            wave = measure(wave, prob, i, L)

        result = ent(wave, n, L, L // 2)
        von[t] = result[0]
        renyi[t] = result[1]
        result = logneg(wave, n, partition)
        neg[t] = result[0]
        mut[t] = result[1]
        mutr[t] = result[2]

    return von, renyi, neg, mut, mutr
コード例 #4
0
ファイル: util.py プロジェクト: nikitinvv/gwp2d
def checkerboard(array, inverse=False):
    """In-place FFTshift for even sized grids only.
    If and only if the dimensions of `array` are even numbers, flipping the
    signs of input signal in an alternating pattern before an FFT is equivalent
    to shifting the zero-frequency component to the center of the spectrum
    before the FFT.
    """
    def g(x):
        return 1 - 2 * (x % 2)

    for i in range(2):
        array = cp.moveaxis(array, i, -1)
        array *= g(cp.arange(array.shape[-1]) + 1)
        if inverse:
            array *= g(array.shape[-1] // 2)
        array = cp.moveaxis(array, -1, i)
    return array
コード例 #5
0
def move_broadcast_axes_to_front(ioperands, subscripts):
    broadcasted_operands = []
    broadcasted_subscripts = []
    for operand, subscript in six.moves.zip(ioperands, subscripts):
        if '@' in subscript:
            ellipsis_pos = subscript.find('@')
            source_axes = list(six.moves.range(ellipsis_pos))
            destination_axes = [i - ellipsis_pos for i in source_axes]
            operand = cupy.moveaxis(operand, source_axes, destination_axes)
            subscript = subscript[ellipsis_pos:] + subscript[:ellipsis_pos]
        broadcasted_operands.append(operand)
        broadcasted_subscripts.append(subscript)
    return broadcasted_operands, broadcasted_subscripts
コード例 #6
0
ファイル: adapt_rgb.py プロジェクト: grlee77/cucim
def each_channel(image_filter, image, *args, **kwargs):
    """Return color image by applying `image_filter` on channels of `image`.

    Note that this function is intended for use with `adapt_rgb`.

    Parameters
    ----------
    image_filter : function
        Function that filters a gray-scale image.
    image : array
        Input image.
    """
    c_new = [
        image_filter(c, *args, **kwargs) for c in cp.moveaxis(image, -1, 0)
    ]
    return cp.stack(c_new, axis=-1)
コード例 #7
0
    def agents_neighbour_flat_positions(self):
        """
        For any agent we find 3x3x3 array of their neighbour cells indexes.
        Flatten position in env of an agent will be at the center of this array.
        Result array will be 4 dimensional.
        """
        agents_positions = cp.asarray(self._agents_positions)

        neighbour_positions = cp.expand_dims(agents_positions,
                                             0).repeat(27, axis=0)
        neighbour_positions = cp.moveaxis(neighbour_positions, (0, 1, 2),
                                          (2, 0, 1))
        neighbour_positions = neighbour_positions.reshape(
            (-1, 3, 3, 3, 3)).swapaxes(1, 4)
        neighbour_positions = neighbour_positions + NEIGHBOUR_POSITIONS_DELTA
        neighbour_positions = neighbour_positions.reshape(-1, 3).T
        neighbour_flattened_poses = cp.ravel_multi_index(
            neighbour_positions, self.env.shape)
        return neighbour_flattened_poses.reshape((-1, 3, 3, 3))
コード例 #8
0
def polyvander(x, deg):
    """Computes the Vandermonde matrix of given degree.

    Args:
        x (cupy.ndarray): array of points
        deg (int): degree of the resulting matrix.

    Returns:
        cupy.ndarray: The Vandermonde matrix

    .. seealso:: :func:`numpy.polynomial.polynomial.polyvander`

    """
    deg = cupy.polynomial.polyutils._deprecate_as_int(deg, 'deg')
    if deg < 0:
        raise ValueError('degree must be non-negative')
    if x.ndim == 0:
        x = x.ravel()
    dtype = cupy.float64 if x.dtype.kind in 'biu' else x.dtype
    out = x**cupy.arange(deg + 1, dtype=dtype).reshape((-1, ) + (1, ) * x.ndim)
    return cupy.moveaxis(out, 0, -1)
コード例 #9
0
def calc_transposed_view(ioperand, input_subscript, output_subscript):
    """Calculates 'ij->ji' by cupy.transpose if needed.

    Args:
        ioperand (cupy.ndarray): Array to be transpose.
        input_subscript (str): Specifies the subscripts for input arrays.
        output_subscript (str):
            Specifies the subscripts for output arrays. If input does not
            match output, ``operand`` is transposed so that it matches.
    """

    assert len(set(output_subscript)) == len(output_subscript)
    assert set(input_subscript) == set(output_subscript)

    if input_subscript == output_subscript:
        return ioperand

    source_axes = []
    destination_axes = []
    ellipsis_pos_input = input_subscript.find('@')
    ellipsis_pos_output = output_subscript.find('@')

    for label_pos_output, label in enumerate(output_subscript):
        if label == '@':
            continue
        if ellipsis_pos_input == -1 or label_pos_output < ellipsis_pos_output:
            destination_axes.append(label_pos_output)
        else:
            destination_axes.append(label_pos_output - len(output_subscript))
        label_pos_input = input_subscript.find(label)
        if ellipsis_pos_input == -1 or label_pos_input < ellipsis_pos_input:
            source_axes.append(label_pos_input)
        else:
            source_axes.append(label_pos_input - len(input_subscript))

    return cupy.moveaxis(ioperand, source_axes, destination_axes)
コード例 #10
0
ファイル: algebraic.py プロジェクト: Koliaska/tomomak
    def step(self, model, step_num):
        alpha = self.get_alpha(model, step_num)
        # multiplication
        det_num = model.detector_signal.shape[0]
        for i in range(self.n_slices):
            # get slice
            # ############## may be speeded up if all slices are calc. once out of for cycle
            i1 = int(i * cp.ceil(det_num / self.n_slices))
            i2 = int(min((i + 1) * cp.ceil(det_num / self.n_slices), det_num))
            w_slice = model.detector_geometry[i1:i2]
            wi_slice = self.wi[i1:i2]
            y_slice = model.detector_signal[i1:i2]
            # calculating  correction
            p = signal.get_signal_gpu(model.solution, w_slice)
            dp = y_slice - p
            a = cp.divide(dp, wi_slice)
            a = cp.where(cp.isnan(a), 0, a)

            if self.iter_type == 1:  # SMART
                y_slice = cp.where(y_slice < 1E-20, 0, y_slice)
                a = cp.divide(a, np.abs(y_slice))
                a = cp.where(cp.isnan(a), 0, a)
            correction = alpha / (i2 - i1) * cp.sum(cp.multiply(cp.moveaxis(w_slice, 0, -1), a), axis=-1)
            model._solution = model.solution + correction
コード例 #11
0
ファイル: vector_fields.py プロジェクト: dipy/cudipy
def warp(
    volume,
    d1,
    affine_idx_in=None,
    affine_idx_out=None,
    affine_disp=None,
    out_shape=None,
    *,
    order=1,
    mode="constant",
    coord_axis=-1,
):
    """
    Deforms the input volume under the given transformation. The warped volume
    is computed using is given by:

    (1) warped[i] = volume[ C * d1[A*i] + B*i ]

    where:
    A = affine_idx_in
    B = affine_idx_out
    C = affine_disp
    """
    A = affine_idx_in
    B = affine_idx_out
    C = affine_disp
    if out_shape is None:
        out_shape = volume.shape
    if A is not None:
        A = cupy.asarray(A)
    if B is not None:
        B = cupy.asarray(B)
    if C is not None:
        C = cupy.asarray(C)

    # TODO: reduce number of temporary arrays
    coord_dtype = cupy.promote_types(volume.dtype, np.float32)
    ndim = volume.ndim
    if d1.shape[coord_axis] != ndim:
        raise ValueError("expected a displacement field with shape "
                         "{} along axis {}".format(ndim, coord_axis))
    if A is None:
        xcoords = cupy.meshgrid(
            *[cupy.arange(s, dtype=coord_dtype) for s in out_shape],
            indexing="ij",
            sparse=True,
        )
        Z = cupy.ascontiguousarray(cupy.moveaxis(d1, -1, 0))
    else:
        xcoords = cupy.meshgrid(
            *[cupy.arange(s, dtype=coord_dtype) for s in out_shape],
            indexing="ij",
            sparse=True,
        )
        # Y = mul0(A, xcoords, sh, cupy, lastcol=1)
        Y = _apply_affine_to_field(
            xcoords,
            A[:ndim, :],
            out=None,
            include_translations=True,
            coord_axis=0,
        )

        # for CuPy with non-legacy linear interpolation, don't need to extend d1
        Z = cupy.empty_like(Y)
        if coord_axis == -1:
            for n in range(ndim):
                Z[n, ...] = ndi.map_coordinates(d1[..., n],
                                                Y,
                                                order=1,
                                                mode=mode)
        else:
            for n in range(ndim):
                Z[n, ...] = ndi.map_coordinates(d1[n], Y, order=1, mode=mode)

    if C is not None:
        # Z = mul0(C, Z, sh, cupy, out=Z, lastcol=0)
        Z = _apply_affine_to_field(
            Z,
            C[:ndim, :ndim],
            out=None,
            include_translations=False,
            coord_axis=0,
        )
    if B is not None:
        # Z += mul0(B, xcoords, sh, cupy, lastcol=1)
        Z += _apply_affine_to_field(
            xcoords,
            B[:ndim, :],
            out=None,
            include_translations=True,
            coord_axis=0,
        )
    else:
        if A is None:
            for n in range(ndim):
                Z[n, ...] += xcoords[n]
        else:
            Z += Y
    return ndi.map_coordinates(volume, Z, order=order, mode=mode)
コード例 #12
0
ファイル: vector_fields.py プロジェクト: dipy/cudipy
def simplify_warp_function(
    d,
    affine_idx_in,
    affine_idx_out,
    affine_disp,
    out_shape,
    *,
    mode="constant",
    coord_axis=-1,
):
    """
    Simplifies a nonlinear warping function combined with an affine transform

    Modifies the given deformation field by incorporating into it
    an affine transformation and voxel-to-space transforms associated with
    the discretization of its domain and codomain.

    The resulting transformation may be regarded as operating on the
    image spaces given by the domain and codomain discretization.
    More precisely, the resulting transform is of the form:

    (1) T[i] = W * d[U * i] + V * i

    Where U = affine_idx_in, V = affine_idx_out, W = affine_disp.

    Parameters
    ----------
    d : array, shape (S', R', C', 3)
        the non-linear part of the transformation (displacement field)
    affine_idx_in : array, shape (4, 4)
        the matrix U in eq. (1) above
    affine_idx_out : array, shape (4, 4)
        the matrix V in eq. (1) above
    affine_disp : array, shape (4, 4)
        the matrix W in eq. (1) above
    out_shape : array, shape (3,)
        the number of slices, rows and columns of the sampling grid

    Returns
    -------
    out : array, shape = out_shape
        the deformation field `out` associated with `T` in eq. (1) such that:
        T[i] = i + out[i]

    Notes
    -----
    Both the direct and inverse transforms of a DiffeomorphicMap can be written
    in this form:

    Direct:  Let D be the voxel-to-space transform of the domain's
             discretization, P be the pre-align matrix, Rinv the space-to-voxel
             transform of the reference grid (the grid the displacement field
             is defined on) and Cinv be the space-to-voxel transform of the
             codomain's discretization. Then, for each i in the domain's grid,
             the direct transform is given by

             (2) T[i] = Cinv * d[Rinv * P * D * i] + Cinv * P * D * i

             and we identify U = Rinv * P * D, V = Cinv * P * D, W = Cinv

    Inverse: Let C be the voxel-to-space transform of the codomain's
             discretization, Pinv be the inverse of the pre-align matrix, Rinv
             the space-to-voxel transform of the reference grid (the grid the
             displacement field is defined on) and Dinv be the space-to-voxel
             transform of the domain's discretization. Then, for each j in the
             codomain's grid, the inverse transform is given by

             (3) Tinv[j] = Dinv * Pinv * d[Rinv * C * j] + Dinv * Pinv * C * j

             and we identify U = Rinv * C, V = Dinv * Pinv * C, W = Dinv * Pinv

    """
    if coord_axis not in [0, -1]:
        raise ValueError("coord_axis must be 0 or -1")
    ndim = d.shape[coord_axis]
    U = affine_idx_in
    V = affine_idx_out
    W = affine_disp
    # TODO: reduce number of temporary arrays
    coord_dtype = cupy.promote_types(d.dtype, np.float32)
    if U is None:
        xcoords = cupy.meshgrid(
            *[cupy.arange(s, dtype=coord_dtype) for s in d.shape[:-1]],
            indexing="ij",
            sparse=True,
        )
        if coord_axis == 0:
            Z = d.copy()
        else:
            Z = cupy.ascontiguousarray(cupy.moveaxis(d, -1, 0))
    else:
        xcoords = cupy.meshgrid(
            *[cupy.arange(s, dtype=coord_dtype) for s in d.shape[:-1]],
            indexing="ij",
            sparse=True,
        )
        # Y = mul0(A, xcoords, sh, cupy, lastcol=1)
        Y = _apply_affine_to_field(
            xcoords,
            U[:ndim, :],
            out=None,
            include_translations=True,
            coord_axis=0,
        )

        # for CuPy with non-legacy linear interpolation, don't need to extend d
        Z = cupy.empty_like(Y)
        if coord_axis == 0:
            for n in range(ndim):
                Z[n, ...] = ndi.map_coordinates(d[n], Y, order=1, mode=mode)
        else:
            for n in range(ndim):
                Z[n, ...] = ndi.map_coordinates(d[..., n],
                                                Y,
                                                order=1,
                                                mode=mode)

    if W is not None:
        # Z = mul0(C, Z, sh, cupy, out=Z, lastcol=0)
        Z = _apply_affine_to_field(
            Z,
            W[:ndim, :ndim],
            out=None,
            include_translations=False,
            coord_axis=0,
        )

    if V is not None:
        Z += _apply_affine_to_field(
            xcoords,
            V[:ndim, :],
            out=None,
            include_translations=True,
            coord_axis=0,
        )
        for n in range(ndim):
            Z[n, ...] -= xcoords[
                n]  # TODO: just subtract one from last column of V instead?
    if coord_axis == -1:
        Z = cupy.moveaxis(Z, 0, -1)
    if not Z.flags.c_contiguous:
        Z = cupy.ascontiguousarray(Z)
    return Z
コード例 #13
0
ファイル: vector_fields.py プロジェクト: dipy/cudipy
def invert_vector_field_fixed_point(
    d,
    d_world2grid,
    spacing,
    max_iter,
    tol,
    start=None,
    *,
    coord_axis=-1,
    print_stats=False,
):
    """Computes the inverse of a 3D displacement fields

    Computes the inverse of the given 3-D displacement field d using the
    fixed-point algorithm [1].

    [1] Chen, M., Lu, W., Chen, Q., Ruchala, K. J., & Olivera, G. H. (2008).
        A simple fixed-point approach to invert a deformation field.
        Medical Physics, 35(1), 81. doi:10.1118/1.2816107

    Parameters
    ----------
    d : array, shape (S, R, C, 3)
        the 3-D displacement field to be inverted
    d_world2grid : array, shape (4, 4)
        the space-to-grid transformation associated to the displacement field
        d (transforming physical space coordinates to voxel coordinates of the
        displacement field grid)
    spacing :array, shape (3,)
        the spacing between voxels (voxel size along each axis)
    max_iter : int
        maximum number of iterations to be performed
    tol : float
        maximum tolerated inversion error
    start : array, shape (S, R, C)
        an approximation to the inverse displacement field (if no approximation
        is available, None can be provided and the start displacement field
        will be zero)

    Returns
    -------
    p : array, shape (S, R, C, 3)
        the inverse displacement field

    Notes
    -----
    We assume that the displacement field is an endomorphism so that the shape
    and voxel-to-space transformation of the inverse field's discretization is
    the same as those of the input displacement field. The 'inversion error' at
    iteration t is defined as the mean norm of the displacement vectors of the
    input displacement field composed with the inverse at iteration t.
    """

    ndim = d.shape[coord_axis]
    if coord_axis != 0:
        d = cupy.moveaxis(d, coord_axis, 0)

    if start is not None:
        if coord_axis != 0:
            start = cupy.moveaxis(start, coord_axis, 0)
            if start.shape != d.shape:
                raise ValueError("start must have the same shape as d")
            p = cupy.ascontiguousarray(start)
        else:
            p = start.copy()
    else:
        p = cupy.zeros_like(d)
    q = cupy.empty_like(d)

    if spacing.dtype != q.dtype:
        spacing = spacing.astype(q.dtype)

    if d_world2grid is not None:
        d_world2grid = cupy.asarray(d_world2grid)
        if not d_world2grid.flags.c_contiguous:
            d_world2grid = cupy.ascontiguousarray(d_world2grid)

    # for efficiency, precompute xcoords, Y and Z here instead of repeatedly
    # doing so inside of compose_vector_fields
    xcoords = cupy.meshgrid(
        *[cupy.arange(s, dtype=d.real.dtype) for s in d.shape[1:]],
        indexing="ij",
        sparse=True,
    )
    Y = cupy.empty_like(d)
    Z = cupy.empty_like(d)

    iter_count = 0
    difmag = 1
    error = 1 + tol
    while (0.1 < difmag) and (iter_count < max_iter) and (tol < error):
        if iter_count == 0:
            epsilon = 0.75
        else:
            epsilon = 0.5
        q, _stats = compose_vector_fields(
            p,
            d,
            None,
            d_world2grid,
            1.0,
            comp=q,
            coord_axis=0,
            omit_stats=True,
            xcoords=xcoords,
            Y=Y,
            Z=Z,
        )
        difmag = 0
        error = 0

        # could make special case 2d/3d elementwise kernel for computing norms
        norms = q[0] / spacing[0]
        norms *= norms
        for n in range(1, ndim):
            tmp = q[n] / spacing[n]
            norms += tmp * tmp
        cupy.sqrt(norms, out=norms)

        error = float(norms.sum())
        difmag = float(norms.max())
        maxlen = difmag * epsilon
        _norm_thresh_kernel(q, norms[np.newaxis, ...], epsilon, maxlen, p)

        error /= norms.size
        iter_count += 1

    if print_stats:
        stats = np.empty((2, ), dtype=float)
        stats[0] = error
        stats[1] = iter_count
        print(f"stats={stats}, diffmag={difmag}")
    if coord_axis != 0:
        p = cupy.moveaxis(p, 0, coord_axis)
    return p  # , q, norms, tmp1, tmp2, epsilon, maxlen
コード例 #14
0
ファイル: vector_fields.py プロジェクト: dipy/cudipy
def _apply_affine_to_field(field,
                           affine,
                           out=None,
                           include_translations=False,
                           coord_axis=-1):
    """Reorient a vector field.

    Parameters
    ----------
    field : cupy.ndarray
        The vector displacement field. Should have ndim + 1 dimensions with
        shape (ndim) on the first axis. Alternatively it can be a list of
        length `ndim` where each array corresponds to one coordinate
        vector. This type of list inputs allows for field to be a
        coordinate array as returned by meshgrid (optionally using the
        `sparse=True` option to conserve memory).
    affine : cupy.ndarray
        affine should be a transformation matrix with shape (ndim, ndim).
        It can also be a (ndim + 1, ndim + 1) affine matrix, but in this
        case,  only the upper left (ndim, ndim) portion of the matrix will
        be applied.
    out : cupy.ndarray or None
        The output array (same shape as `field`). Note that in-place
        reorientation is not supported (i.e. `out` cannot be the same array
        as `field`).

    Returns
    -------
    reoriented : cupy.ndarray
        The reoriented displacement field.

    """
    # TODO: remove support for dimensions as last axis instead of first?
    #       that would simplify the function. first axis is more efficient for
    #       C-contiguous arrays
    if coord_axis not in [0, -1]:
        raise ValueError("coord_axis must be 0 or -1.")
    if isinstance(field, cupy.ndarray):
        # field is a single, dense ndarray
        ndim = field.ndim - 1
        if field.shape[coord_axis] != ndim:
            print(
                f"field.shape={field.shape}, ndim={ndim}, coord_axis={coord_axis}"
            )
            raise ValueError("shape mismatch")

        if field.ndim != ndim + 1:
            raise ValueError("invalid field")
        if not field.dtype.kind == "f":
            raise ValueError("field must having floating point dtype")
        if coord_axis == 0:
            field = tuple([f for f in field])
        else:
            field = tuple(
                [cupy.ascontiguousarray(field[..., n]) for n in range(ndim)])
    else:
        ndim = len(field)
    field_dtype = field[0].dtype

    if include_translations:
        affine_shape = (ndim, ndim + 1)
    else:
        affine_shape = (ndim, ndim)
    affine = cupy.asarray(affine, dtype=field_dtype, order="C")
    if affine.shape == (ndim + 1, ndim + 1):
        affine = affine[:affine_shape[0], :affine_shape[1]]
        if not affine.flags.c_contiguous:
            affine = cupy.ascontiguousarray(affine)
    if affine.shape != affine_shape:
        raise ValueError(
            "expected anaffine array with shape {}".format(affine_shape))

    out_shape = (ndim, ) + tuple([field[n].shape[n] for n in range(ndim)])
    if out is None:
        out = cupy.empty(out_shape, dtype=field_dtype, order="C")
    else:
        if out.shape != field.shape or out.dtype != field_dtype:
            raise ValueError(
                "out and field must have matching shape and dtype")
        if not out.flags.c_contiguous:
            raise ValueError("out must be C contiguous")

    # Note: affine must be contiguous.
    #       field and out do not have to be contiguous, but performance
    #       will be better if they are.
    if ndim == 3:
        if include_translations:
            kernel = aff_kernel_3d
        else:
            kernel = reorient_kernel_3d
        # args = field + (affine, out[0], out[1], out[2])
        kernel(*field, affine, out[0], out[1], out[2])
    elif ndim == 2:
        if include_translations:
            kernel = aff_kernel_2d
        else:
            kernel = reorient_kernel_2d
        kernel(*field, affine, out[0], out[1])

    if coord_axis == -1:
        out = cupy.moveaxis(out, 0, -1)
    return out
コード例 #15
0
ファイル: vector_fields.py プロジェクト: dipy/cudipy
def compose_vector_fields(
    d1,
    d2,
    premult_index,
    premult_disp,
    time_scaling,
    comp=None,
    order=1,
    *,
    coord_axis=-1,
    omit_stats=False,
    xcoords=None,
    Y=None,
    Z=None,
):
    if comp is None:
        comp = cupy.empty_like(d1, order="C")

    # need vector elements on first axis, not last
    if coord_axis != 0:
        d1 = cupy.ascontiguousarray(cupy.moveaxis(d1, -1, 0))
        d2 = cupy.ascontiguousarray(cupy.moveaxis(d2, -1, 0))
    else:
        if not d1.flags.c_contiguous:
            d1 = cupy.ascontiguousarray(d1)
        if not d2.flags.c_contiguous:
            d2 = cupy.ascontiguousarray(d2)
    ndim = d1.shape[0]
    B = premult_disp
    A = premult_index
    t = time_scaling

    if xcoords is None:
        xcoords = cupy.meshgrid(
            *[cupy.arange(s, dtype=d1.real.dtype) for s in d1.shape[1:]],
            indexing="ij",
            sparse=True,
        )

    # TODO: reduce number of temporary arrays
    if ndim in [2, 3]:
        if Y is None:
            Y = cupy.empty_like(d1)
        if A is None:
            if B is None:
                if ndim == 3:
                    composeNone_3d(
                        d1[0],
                        d1[1],
                        d1[2],
                        xcoords[0],
                        xcoords[1],
                        xcoords[2],
                        Y[0],
                        Y[1],
                        Y[2],
                    )
                else:
                    composeNone_2d(d1[0], d1[1], xcoords[0], xcoords[1], Y[0],
                                   Y[1])
            else:
                B = cupy.asarray(B[:ndim, :ndim], dtype=d1.dtype, order="C")
                if ndim == 3:
                    composeB_3d(
                        d1[0],
                        d1[1],
                        d1[2],
                        xcoords[0],
                        xcoords[1],
                        xcoords[2],
                        B,
                        Y[0],
                        Y[1],
                        Y[2],
                    )
                else:
                    composeB_2d(d1[0], d1[1], xcoords[0], xcoords[1], B, Y[0],
                                Y[1])
        elif B is None:
            A = cupy.asarray(A[:ndim, :], dtype=d1.dtype, order="C")
            if ndim == 3:
                composeA_3d(xcoords[0], xcoords[1], xcoords[2], A, Y[0], Y[1],
                            Y[2])
            else:
                composeA_2d(xcoords[0], xcoords[1], A, Y[0], Y[1])
        else:
            A = cupy.asarray(A[:ndim, :], dtype=d1.dtype, order="C")
            B = cupy.asarray(B[:ndim, :ndim], dtype=d1.dtype, order="C")
            if ndim == 3:
                composeAB_3d(
                    d1[0],
                    d1[1],
                    d1[2],
                    xcoords[0],
                    xcoords[1],
                    xcoords[2],
                    B,
                    A,
                    Y[0],
                    Y[1],
                    Y[2],
                )
            else:
                composeAB_2d(d1[0], d1[1], xcoords[0], xcoords[1], B, A, Y[0],
                             Y[1])
    else:
        if B is None:
            d1tmp = d1.copy()  # have to copy to avoid modification of d1
        else:
            d1tmp = _apply_affine_to_field(d1,
                                           B[:ndim, :ndim],
                                           include_translations=False,
                                           coord_axis=0)

        if A is None:
            Y = d1tmp
            for n in range(ndim):
                Y[n] += xcoords[n]
        else:
            # Y = mul0(A, xcoords, sh, cupy, lastcol=1)
            Y = _apply_affine_to_field(xcoords,
                                       A[:ndim, :],
                                       include_translations=True,
                                       coord_axis=0)
            Y += d1tmp

    if Z is None:
        Z = cupy.empty_like(Y)
    for n in range(ndim):
        Z[n, ...] = ndi.map_coordinates(d2[n], Y, order=1, mode="constant")

    if coord_axis == 0:
        res = comp
    else:
        res = cupy.empty_like(Z)

    if omit_stats and ndim in [2, 3]:
        _shape = cupy.asarray([d1.shape[1 + n] - 1 for n in range(ndim)],
                              dtype=cupy.int32)
        if ndim == 3:
            _comp_apply_masked_time_scaling_3d(
                d1[0],
                d1[1],
                d1[2],
                Y[0],
                Y[1],
                Y[2],
                Z[0],
                Z[1],
                Z[2],
                t,
                _shape,
                res[0],
                res[1],
                res[2],
            )
        else:
            _comp_apply_masked_time_scaling_2d(d1[0], d1[1], Y[0], Y[1], Z[0],
                                               Z[1], t, _shape, res[0], res[1])
    else:

        # TODO: declare count as boolean?
        count = cupy.zeros(Z.shape[1:], dtype=np.int32)

        # We now compute:
        #    res = d1 + t * Z
        #    except that res = 0 where either coordinate in
        #    interpolating Y was outside the displacement extent
        for n in range(ndim):
            _comp_apply_masked_time_scaling_nd(d1[n], Y[n], Z[n], t,
                                               d1.shape[1 + n] - 1, res[n],
                                               count)

        # nnz corresponds to the number of points in comp inside the domain
        count = count > 0  # remove after init count as boolean
        if not omit_stats:
            nnz = res.size // ndim - cupy.count_nonzero(count)
        res *= ~count[np.newaxis, ...]

    if omit_stats:
        stats = None
    else:
        # compute the stats
        stats = cupy.empty((3, ), dtype=float)
        nn = res[0] * res[0]
        for n in range(1, ndim):
            nn += res[n] * res[n]
        # TODO: do we want stats to be a GPU array or CPU array?
        stats[0] = cupy.sqrt(nn.max())
        mean_norm = nn.sum() / nnz
        stats[1] = cupy.sqrt(mean_norm)
        nn *= nn
        stats[2] = cupy.sqrt(nn.sum() / nnz - mean_norm * mean_norm)

    if coord_axis != 0:
        res = cupy.moveaxis(res, 0, -1)
        comp[...] = res

    return comp, stats
コード例 #16
0
def gen_data_npz(fimg, img, mask, config, ntiles=1000, save_dir='train'):
    """
    Extract random patches from cupy arrays.
    Args:
        fimg (str): data filename
        img (cupy.array): cupy array with data
        mask (cupy.array): cupy array with mask
        save_dir (str): directory to save output
    Return:
        save dataset to save_dir.
    ----------
    Example
    ----------
        gen_data_npz('image.tif', arr, mask, config, 8000, 'output')
    """
    # set dimensions of the input image array, and get desired tile size
    z_dim, x_dim, y_dim = img.shape
    tsz = config.TILE_SIZE

    # placeholders for final datasets
    img_cp = cp.empty((ntiles, tsz, tsz, z_dim), dtype=cp.float32)
    mask_np = np.empty((ntiles, tsz, tsz, config.N_CLASSES), dtype=np.float16)

    # generate n number of tiles
    for i in tqdm(range(ntiles)):

        # Generate random integers from image
        xc = random.randint(0, x_dim - tsz)
        yc = random.randint(0, y_dim - tsz)

        # verify data is not on nodata region
        while cp.any(img[:, xc:(xc + tsz),
                         yc:(yc + tsz)] == config.NODATA_VAL):
            xc = random.randint(0, x_dim - tsz)
            yc = random.randint(0, y_dim - tsz)

        # change order to (h, w, c)
        tile_img = cp.moveaxis(img[:, xc:(xc + tsz), yc:(yc + tsz)], 0, -1)

        # TODO: replace with cuml One-hot encoder on future date when they fix
        # a bug on the output types. Using to_categorical in the meantime
        # Converts labels into one-hot encoding labels
        tile_mask = to_categorical(cp.asnumpy(mask[xc:(xc + tsz),
                                                   yc:(yc + tsz)]),
                                   num_classes=config.N_CLASSES,
                                   dtype='float16')

        # maybe standardize here? depends on performance of single img vs batch
        img_cp[i, :, :, :] = tile_img
        mask_np[i, :, :, :] = tile_mask

    # normalize
    if config.NORMALIZE:
        img_cp = img_cp / config.normalization_factor

    # standardize
    if config.STANDARDIZE:
        img_cp = batch_normalize(img_cp, axis=(0, 1), c=1e-8)

    # save dataset into local disk, npz format with x and y labels
    cp.savez(f'{save_dir}/{fimg[:-4]}.npz', x=img_cp, y=cp.asarray(mask_np))
コード例 #17
0
def _multi_svd_norm(x, row_axis, col_axis, op):
    y = cupy.moveaxis(x, (row_axis, col_axis), (-2, -1))
    result = op(_decomposition.svd(y, compute_uv=False), axis=-1)
    return result
コード例 #18
0
ファイル: filters.py プロジェクト: Rheinwalt/structure-tensor
def gaussian_filter(input,
                    sigma,
                    order=0,
                    output=None,
                    mode="reflect",
                    cval=0.0,
                    truncate=4.0):
    """Multidimensional Gaussian filter.

    Based on SciPy implementation.

    Arguments:
        input: array_like
            The input array.
        sigma: scalar or sequence of scalars
            Standard deviation for Gaussian kernel. The standard
            deviations of the Gaussian filter are given for each axis as a
            sequence, or as a single number, in which case it is equal for
            all axes.
        order: int or sequence of ints, optional
            The order of the filter along each axis is given as a sequence
            of integers, or as a single number.  An order of 0 corresponds
            to convolution with a Gaussian kernel. A positive order
            corresponds to convolution with that derivative of a Gaussian.
        output: cupy.ndarray or None
            The array in which to place the output.
        mode: str
            The array borders are handled according to the given mode
            (``'reflect'``, ``'constant'``, ``'nearest'``, ``'mirror'``,
            ``'wrap'``). Default is ``'reflect'``.
        cval: scalar
            Value to fill past edges of input if mode is ``constant``.
            Default is ``0.0``.
        truncate : float
            Truncate the filter at this many standard deviations.
            Default is 4.0.

    Returns:
        gaussian_filter: cupy.ndarray
            Returned array of same shape as `input`.

    Notes:
        The multidimensional filter is implemented as a sequence of
        one-dimensional convolution filters. The intermediate arrays are
        stored in the same data type as the output. Therefore, for output
        types with a limited precision, the results may be imprecise
        because intermediate results may be stored with insufficient
        precision.
    """
    input = cupy.array(input)
    if output is None:
        output = cupy.zeros_like(input)

    # Code from Scipy function.
    orders = _ni_support._normalize_sequence(order, input.ndim)
    sigmas = _ni_support._normalize_sequence(sigma, input.ndim)
    modes = _ni_support._normalize_sequence(mode, input.ndim)
    axes = list(range(input.ndim))
    axes = [(axes[ii], sigmas[ii], orders[ii], modes[ii])
            for ii in range(len(axes)) if sigmas[ii] > 1e-15]

    if len(axes) > 0:

        # Create valiable for output.
        out = output
        for axis, sigma, order, mode in axes:

            # Flatten other axis.
            # This seems to improve performance at the cost of some memory.
            out_flat = cupy.moveaxis(out, axis, -1)
            out_shape = out_flat.shape
            out_flat = out_flat.reshape(-1, out_flat.shape[-1])
            input_flat = cupy.moveaxis(input, axis, -1)
            input_flat = input_flat.reshape(-1, input_flat.shape[-1])

            # Do 1D filtering.
            out = gaussian_filter1d(input_flat, sigma, -1, order, out_flat,
                                    mode, cval, truncate)
            out = cupy.moveaxis(out.reshape(out_shape), -1, axis)

            # Swap input and out. This was we only use two arrays.
            tmp = input
            input = out
            out = tmp

        # If there was an even number of iterations we need to copy values from input to output.
        if len(axes) % 2 == 0:
            output[:] = input
    else:
        output[...] = input[...]
    return output
コード例 #19
0
ファイル: _optical_flow.py プロジェクト: mritools/cupyimg
def _ilk(reference_image, moving_image, flow0, radius, num_warp, gaussian,
         prefilter):
    """Iterative Lucas-Kanade (iLK) solver for optical flow estimation.

    Parameters
    ----------
    reference_image : ndarray, shape (M, N[, P[, ...]])
        The first gray scale image of the sequence.
    moving_image : ndarray, shape (M, N[, P[, ...]])
        The second gray scale image of the sequence.
    flow0 : ndarray, shape (reference_image.ndim, M, N[, P[, ...]])
        Initialization for the vector field.
    radius : int
        Radius of the window considered around each pixel.
    num_warp : int
        Number of times moving_image is warped.
    gaussian : bool
        if True, a gaussian kernel is used for the local
        integration. Otherwise, a uniform kernel is used.
    prefilter : bool
        Whether to prefilter the estimated optical flow before each
        image warp. This helps to remove potential outliers.

    Returns
    -------
    flow : ndarray, shape ((reference_image.ndim, M, N[, P[, ...]])
        The estimated optical flow components for each axis.

    """
    dtype = reference_image.dtype
    ndim = reference_image.ndim
    size = 2 * radius + 1

    if gaussian:
        sigma = ndim * (size / 4, )
        filter_func = partial(ndi.gaussian_filter, sigma=sigma, mode="mirror")
    else:
        filter_func = partial(ndi.uniform_filter,
                              size=ndim * (size, ),
                              mode="mirror")

    flow = flow0
    # For each pixel location (i, j), the optical flow X = flow[:, i, j]
    # is the solution of the ndim x ndim linear system
    # A[i, j] * X = b[i, j]
    A = cp.zeros(reference_image.shape + (ndim, ndim), dtype=dtype)
    b = cp.zeros(reference_image.shape + (ndim, ), dtype=dtype)

    grid = cp.meshgrid(
        *[cp.arange(n, dtype=dtype) for n in reference_image.shape],
        indexing="ij",
        sparse=True,
    )

    for _ in range(num_warp):
        if prefilter:
            flow = ndi.filters.median_filter(flow, (1, ) + ndim * (3, ))

        moving_image_warp = warp(moving_image,
                                 get_warp_points(grid, flow),
                                 mode="nearest")
        grad = cp.stack(cp.gradient(moving_image_warp), axis=0)
        error_image = ((grad * flow).sum(axis=0) + reference_image -
                       moving_image_warp)

        # Local linear systems creation
        for i, j in combinations_with_replacement(range(ndim), 2):
            A[..., i, j] = A[..., j, i] = filter_func(grad[i] * grad[j])

        for i in range(ndim):
            b[..., i] = filter_func(grad[i] * error_image)

        # Don't consider badly conditioned linear systems
        idx = abs(cp.linalg.det(A)) < 1e-14
        A[idx] = cp.eye(ndim, dtype=dtype)
        b[idx] = 0

        # Solve the local linear systems
        flow = cp.moveaxis(cp.linalg.solve(A, b), ndim, 0)

    return flow
コード例 #20
0
def sosfilt(
    sos,
    x,
    axis=-1,
    zi=None,
):
    """
    Filter data along one dimension using cascaded second-order sections.
    Filter a data sequence, `x`, using a digital IIR filter defined by
    `sos`.

    Parameters
    ----------
    sos : array_like
        Array of second-order filter coefficients, must have shape
        ``(n_sections, 6)``. Each row corresponds to a second-order
        section, with the first three columns providing the numerator
        coefficients and the last three providing the denominator
        coefficients.
    x : array_like
        An N-dimensional input array.
    axis : int, optional
        The axis of the input data array along which to apply the
        linear filter. The filter is applied to each subarray along
        this axis.  Default is -1.
    zi : array_like, optional
        Initial conditions for the cascaded filter delays.  It is a (at
        least 2D) vector of shape ``(n_sections, ..., 2, ...)``, where
        ``..., 2, ...`` denotes the shape of `x`, but with ``x.shape[axis]``
        replaced by 2.  If `zi` is None or is not given then initial rest
        (i.e. all zeros) is assumed.
        Note that these initial conditions are *not* the same as the initial
        conditions given by `lfiltic` or `lfilter_zi`.

    Returns
    -------
    y : ndarray
        The output of the digital filter.
    zf : ndarray, optional
        If `zi` is None, this is not returned, otherwise, `zf` holds the
        final filter delay values.
    See Also
    --------
    zpk2sos, sos2zpk, sosfilt_zi, sosfiltfilt, sosfreqz

    Notes
    -----
    WARNING: This is an experimental API and is prone to change in future
    versions of cuSignal.

    The filter function is implemented as a series of second-order filters
    with direct-form II transposed structure. It is designed to minimize
    numerical precision errors for high-order filters.

    Limitations
    -----------
    1. The number of n_sections must be less than 513.
    2. The number of samples must be greater than the number of sections

    Examples
    --------
    sosfilt is a stable alternative to `lfilter` as using 2nd order sections
    reduces numerical error. We are working on building out sos filter output,
    so please submit GitHub feature requests as needed. You can also generate
    a filter on CPU with scipy.signal and then move that to GPU for actual
    filtering operations with `cp.asarray`.

    Plot a 13th-order filter's impulse response using both `sosfilt`:
    >>> from scipy import signal
    >>> import cusignal
    >>> import cupy as cp
    >>> # Generate filter on CPU with Scipy.Signal
    >>> sos = signal.ellip(13, 0.009, 80, 0.05, output='sos')
    >>> # Move data to GPU
    >>> sos = cp.asarray(sos)
    >>> x = cp.random.randn(100_000_000)
    >>> y = cusignal.sosfilt(sos, x)
    """

    x = cp.asarray(x)
    if x.ndim == 0:
        raise ValueError("x must be at least 1D")

    sos, n_sections = _validate_sos(sos)
    sos = cp.asarray(sos)

    x_zi_shape = list(x.shape)
    x_zi_shape[axis] = 2
    x_zi_shape = tuple([n_sections] + x_zi_shape)
    inputs = [sos, x]

    if zi is not None:
        inputs.append(np.asarray(zi))

    dtype = cp.result_type(*inputs)

    if dtype.char not in "fdgFDGO":
        raise NotImplementedError("input type '%s' not supported" % dtype)
    if zi is not None:
        zi = cp.array(zi, dtype)  # make a copy so that we can operate in place
        if zi.shape != x_zi_shape:
            raise ValueError("Invalid zi shape. With axis=%r, an input with "
                             "shape %r, and an sos array with %d sections, zi "
                             "must have shape %r, got %r." %
                             (axis, x.shape, n_sections, x_zi_shape, zi.shape))
        return_zi = True
    else:
        zi = cp.zeros(x_zi_shape, dtype=dtype)
        return_zi = False

    axis = axis % x.ndim  # make positive
    x = cp.moveaxis(x, axis, -1)
    zi = cp.moveaxis(zi, [0, axis + 1], [-2, -1])
    x_shape, zi_shape = x.shape, zi.shape
    x = cp.reshape(x, (-1, x.shape[-1]))
    x = cp.array(x, dtype, order="C")  # make a copy, can modify in place
    zi = cp.ascontiguousarray(cp.reshape(zi, (-1, n_sections, 2)))
    sos = sos.astype(dtype, copy=False)

    max_smem = _get_max_smem()
    max_tpb = _get_max_tpb()

    # Determine how much shared memory is needed
    out_size = sos.shape[0]
    z_size = zi.shape[1] * zi.shape[2]
    sos_size = sos.shape[0] * sos.shape[1]
    shared_mem = (out_size + z_size + sos_size) * x.dtype.itemsize

    if shared_mem > max_smem:
        max_sections = (max_smem // (1 + zi.shape[2] + sos.shape[1]) //
                        x.dtype.itemsize)
        raise ValueError("The number of sections ({}), requires too much "
                         "shared memory ({}B) > ({}B). \n"
                         "\n**Max sections possible ({})**".format(
                             sos.shape[0], shared_mem, max_smem, max_sections))

    if sos.shape[0] > max_tpb:
        raise ValueError("The number of sections ({}), must be less "
                         "than max threads per block ({})".format(
                             sos.shape[0], max_tpb))

    if sos.shape[0] > x.shape[1]:
        raise ValueError("The number of samples ({}), must be greater "
                         "than the number of sections ({})".format(
                             x.shape[1], sos.shape[0]))

    _sosfilt(sos, x, zi)

    x.shape = x_shape
    x = cp.moveaxis(x, -1, axis)
    if return_zi:
        zi.shape = zi_shape
        zi = cp.moveaxis(zi, [-2, -1], [0, axis + 1])
        out = (x, zi)
    else:
        out = x

    return out
コード例 #21
0
ファイル: _moments.py プロジェクト: grlee77/cucim
def moments_coords_central(coords, center=None, order=3):
    """Calculate all central image moments up to a certain order.

    The following properties can be calculated from raw image moments:
     * Area as: ``M[0, 0]``.
     * Centroid as: {``M[1, 0] / M[0, 0]``, ``M[0, 1] / M[0, 0]``}.

    Note that raw moments are neither translation, scale nor rotation
    invariant.

    Parameters
    ----------
    coords : (N, D) floating point or uint8 array
        Array of N points that describe an image of D dimensionality in
        Cartesian space. A tuple of coordinates as returned by
        ``cp.nonzero`` is also accepted as input.
    center : tuple of float, optional
        Coordinates of the image centroid. This will be computed if it
        is not provided.
    order : int, optional
        Maximum order of moments. Default is 3.

    Returns
    -------
    Mc : (``order + 1``, ``order + 1``, ...) array
        Central image moments. (D dimensions)

    References
    ----------
    .. [1] Johannes Kilian. Simple Image Analysis By Moments. Durham
           University, version 0.2, Durham, 2001.

    Examples
    --------
    >>> import cupy as cp
    >>> from cucim.skimage.measure import moments_coords_central
    >>> coords = cp.array([[row, col]
    ...                    for row in range(13, 17)
    ...                    for col in range(14, 18)])
    >>> moments_coords_central(coords)
    array([[16.,  0., 20.,  0.],
           [ 0.,  0.,  0.,  0.],
           [20.,  0., 25.,  0.],
           [ 0.,  0.,  0.,  0.]])

    As seen above, for symmetric objects, odd-order moments (columns 1 and 3,
    rows 1 and 3) are zero when centered on the centroid, or center of mass,
    of the object (the default). If we break the symmetry by adding a new
    point, this no longer holds:

    >>> coords2 = cp.concatenate((coords, [[17, 17]]), axis=0)
    >>> cp.round(moments_coords_central(coords2),
    ...          decimals=2)  # doctest: +NORMALIZE_WHITESPACE
    array([[17.  ,  0.  , 22.12, -2.49],
           [ 0.  ,  3.53,  1.73,  7.4 ],
           [25.88,  6.02, 36.63,  8.83],
           [ 4.15, 19.17, 14.8 , 39.6 ]])

    Image moments and central image moments are equivalent (by definition)
    when the center is (0, 0):

    >>> cp.allclose(moments_coords(coords),
    ...             moments_coords_central(coords, (0, 0)))
    True
    """
    if isinstance(coords, tuple):
        # This format corresponds to coordinate tuples as returned by
        # e.g. cp.nonzero: (row_coords, column_coords).
        # We represent them as an npoints x ndim array.
        coords = cp.stack(coords, axis=-1)
    float_dtype = coords.dtype if coords.dtype.kind == 'f' else cp.float64
    check_nD(coords, 2)
    ndim = coords.shape[1]
    if center is None:
        center = cp.mean(coords, axis=0)
    else:
        center = cp.asarray(center, dtype=float_dtype)

    # center the coordinates
    coords = coords.astype(float_dtype, copy=False)
    coords -= center

    # CuPy backend: for efficiency, sum over the last axis
    #               (which is memory contiguous)
    # generate all possible exponents for each axis in the given set of points
    # produces a matrix of shape (order + 1, D, N)
    coords = coords.T
    powers = cp.arange(order + 1, dtype=float_dtype)[:, np.newaxis, np.newaxis]
    coords = coords[cp.newaxis, ...]**powers

    # add extra dimensions for proper broadcasting
    coords = coords.reshape((1, ) * (ndim - 1) + coords.shape)

    calc = cp.moveaxis(coords[..., 0, :], -2, 0)

    for axis in range(1, ndim):
        # isolate each point's axis
        isolated_axis = coords[..., axis, :]

        # rotate orientation of matrix for proper broadcasting
        isolated_axis = cp.moveaxis(isolated_axis, -2, axis)

        # calculate the moments for each point, one axis at a time
        calc = calc * isolated_axis
    # sum all individual point moments to get our final answer
    Mc = cp.sum(calc, axis=-1)

    return Mc
コード例 #22
0
ファイル: test_transpose.py プロジェクト: yuhc/ava-cupy
 def test_moveaxis_invalid5_2(self):
     a = testing.shaped_arange((2, 3, 4), cupy)
     with self.assertRaises(cupy.core._AxisError):
         return cupy.moveaxis(a, [0, 1], [-1, 2])
コード例 #23
0
ファイル: _product.py プロジェクト: takagi/cupy
def cross(a, b, axisa=-1, axisb=-1, axisc=-1, axis=None):
    """Returns the cross product of two vectors.

    The cross product of ``a`` and ``b`` in :math:`R^3` is a vector
    perpendicular to both ``a`` and ``b``.  If ``a`` and ``b`` are arrays
    of vectors, the vectors are defined by the last axis of ``a`` and ``b``
    by default, and these axes can have dimensions 2 or 3.  Where the
    dimension of either ``a`` or ``b`` is 2, the third component of the input
    vector is assumed to be zero and the cross product calculated accordingly.
    In cases where both input vectors have dimension 2, the z-component of
    the cross product is returned.

    Args:
        a (cupy.ndarray): Components of the first vector(s).
        b (cupy.ndarray): Components of the second vector(s).
        axisa (int, optional):
            Axis of ``a`` that defines the vector(s).
            By default, the last axis.
        axisb (int, optional):
            Axis of ``b`` that defines the vector(s).
            By default, the last axis.
        axisc (int, optional):
            Axis of ``c`` containing the cross product vector(s).  Ignored if
            both input vectors have dimension 2, as the return is scalar.
            By default, the last axis.
        axis (int, optional):
            If defined, the axis of ``a``, ``b`` and ``c``
            that defines the vector(s) and cross product(s).
            Overrides ``axisa``, ``axisb`` and ``axisc``.

    Returns:
        cupy.ndarray :
            Vector cross product(s).

    .. seealso:: :func:`numpy.cross`

    """

    if axis is not None:
        axisa, axisb, axisc = (axis,) * 3
    a = cupy.asarray(a)
    b = cupy.asarray(b)
    # Check axisa and axisb are within bounds
    axisa = internal._normalize_axis_index(axisa, a.ndim)
    axisb = internal._normalize_axis_index(axisb, b.ndim)

    # Move working axis to the end of the shape
    a = cupy.moveaxis(a, axisa, -1)
    b = cupy.moveaxis(b, axisb, -1)
    if a.shape[-1] not in (2, 3) or b.shape[-1] not in (2, 3):
        msg = ('incompatible dimensions for cross product\n'
               '(dimension must be 2 or 3)')
        raise ValueError(msg)

    # Create the output array
    shape = cupy.broadcast(a[..., 0], b[..., 0]).shape
    if a.shape[-1] == 3 or b.shape[-1] == 3:
        shape += (3,)
        # Check axisc is within bounds
        axisc = internal._normalize_axis_index(axisc, len(shape))
    dtype = cupy.promote_types(a.dtype, b.dtype)
    cp = cupy.empty(shape, dtype)

    # create local aliases for readability
    a0 = a[..., 0]
    a1 = a[..., 1]
    if a.shape[-1] == 3:
        a2 = a[..., 2]
    b0 = b[..., 0]
    b1 = b[..., 1]
    if b.shape[-1] == 3:
        b2 = b[..., 2]
    if cp.ndim != 0 and cp.shape[-1] == 3:
        cp0 = cp[..., 0]
        cp1 = cp[..., 1]
        cp2 = cp[..., 2]

    if a.shape[-1] == 2:
        if b.shape[-1] == 2:
            # a0 * b1 - a1 * b0
            cupy.multiply(a0, b1, out=cp)
            cp -= a1 * b0
            return cp
        else:
            assert b.shape[-1] == 3
            # cp0 = a1 * b2 - 0  (a2 = 0)
            # cp1 = 0 - a0 * b2  (a2 = 0)
            # cp2 = a0 * b1 - a1 * b0
            cupy.multiply(a1, b2, out=cp0)
            cupy.multiply(a0, b2, out=cp1)
            cupy.negative(cp1, out=cp1)
            cupy.multiply(a0, b1, out=cp2)
            cp2 -= a1 * b0
    else:
        assert a.shape[-1] == 3
        if b.shape[-1] == 3:
            # cp0 = a1 * b2 - a2 * b1
            # cp1 = a2 * b0 - a0 * b2
            # cp2 = a0 * b1 - a1 * b0
            cupy.multiply(a1, b2, out=cp0)
            tmp = a2 * b1
            cp0 -= tmp
            cupy.multiply(a2, b0, out=cp1)
            cupy.multiply(a0, b2, out=tmp)
            cp1 -= tmp
            cupy.multiply(a0, b1, out=cp2)
            cupy.multiply(a1, b0, out=tmp)
            cp2 -= tmp
        else:
            assert b.shape[-1] == 2
            # cp0 = 0 - a2 * b1  (b2 = 0)
            # cp1 = a2 * b0 - 0  (b2 = 0)
            # cp2 = a0 * b1 - a1 * b0
            cupy.multiply(a2, b1, out=cp0)
            cupy.negative(cp0, out=cp0)
            cupy.multiply(a2, b0, out=cp1)
            cupy.multiply(a0, b1, out=cp2)
            cp2 -= a1 * b0

    return cupy.moveaxis(cp, -1, axisc)
コード例 #24
0
def pad(array, pad_width, mode='constant', **kwargs):
    """Pads an array with specified widths and values.

    Args:
      array(cupy.ndarray): The array to pad.
      pad_width(sequence, array_like or int): Number of values padded to the
          edges of each axis. ((before_1, after_1), ... (before_N, after_N))
          unique pad widths for each axis. ((before, after),) yields same
          before and after pad for each axis. (pad,) or int is a shortcut for
          before = after = pad width for all axes. You cannot specify
          ``cupy.ndarray``.
      mode(str or function, optional): One of the following string values or a
          user supplied function

          'constant' (default)
              Pads with a constant value.
          'edge'
              Pads with the edge values of array.
          'linear_ramp'
              Pads with the linear ramp between end_value and the array edge
              value.
          'maximum'
              Pads with the maximum value of all or part of the vector along
              each axis.
          'mean'
              Pads with the mean value of all or part of the vector along each
              axis.
          'median'
              Pads with the median value of all or part of the vector along
              each axis. (Not Implemented)
          'minimum'
              Pads with the minimum value of all or part of the vector along
              each axis.
          'reflect'
              Pads with the reflection of the vector mirrored on the first and
              last values of the vector along each axis.
          'symmetric'
               Pads with the reflection of the vector mirrored along the edge
               of the array.
          'wrap'
              Pads with the wrap of the vector along the axis. The first
              values are used to pad the end and the end values are used to
              pad the beginning.
          'empty'
              Pads with undefined values.
          <function>
              Padding function, see Notes.
      stat_length(sequence or int, optional): Used in 'maximum', 'mean',
          'median', and 'minimum'.  Number of values at edge of each axis used
          to calculate the statistic value.
          ((before_1, after_1), ... (before_N, after_N)) unique statistic
          lengths for each axis. ((before, after),) yields same before and
          after statistic lengths for each axis. (stat_length,) or int is a
          shortcut for before = after = statistic length for all axes.
          Default is ``None``, to use the entire axis. You cannot specify
          ``cupy.ndarray``.
      constant_values(sequence or scalar, optional): Used in 'constant'. The
          values to set the padded values for each axis.
          ((before_1, after_1), ... (before_N, after_N)) unique pad constants
          for each axis.
          ((before, after),) yields same before and after constants for each
          axis.
          (constant,) or constant is a shortcut for before = after = constant
          for all axes.
          Default is 0. You cannot specify ``cupy.ndarray``.
      end_values(sequence or scalar, optional): Used in 'linear_ramp'. The
          values used for the ending value of the linear_ramp and that will
          form the edge of the padded array.
          ((before_1, after_1), ... (before_N, after_N)) unique end values
          for each axis.
          ((before, after),) yields same before and after end
          values for each axis.
          (constant,) or constant is a shortcut for before = after = constant
          for all axes.
          Default is 0. You cannot specify ``cupy.ndarray``.
      reflect_type({'even', 'odd'}, optional): Used in 'reflect', and
          'symmetric'.  The 'even' style is the default with an unaltered
          reflection around the edge value.  For the 'odd' style, the extended
          part of the array is created by subtracting the reflected values from
          two times the edge value.

    Returns:
      cupy.ndarray: Padded array with shape extended by ``pad_width``.

    .. note::
        For an array with rank greater than 1, some of the padding of later
        axes is calculated from padding of previous axes.  This is easiest to
        think about with a rank 2 array where the corners of the padded array
        are calculated by using padded values from the first axis.

        The padding function, if used, should modify a rank 1 array in-place.
        It has the following signature:

        ``padding_func(vector, iaxis_pad_width, iaxis, kwargs)``

        where

        vector (cupy.ndarray)
            A rank 1 array already padded with zeros.  Padded values are
            ``vector[:iaxis_pad_width[0]]`` and
            ``vector[-iaxis_pad_width[1]:]``.
        iaxis_pad_width (tuple)
            A 2-tuple of ints, ``iaxis_pad_width[0]`` represents the number of
            values padded at the beginning of vector where
            ``iaxis_pad_width[1]`` represents the number of values padded at
            the end of vector.
        iaxis (int)
            The axis currently being calculated.
        kwargs (dict)
            Any keyword arguments the function requires.

    Examples
    --------
    >>> a = cupy.array([1, 2, 3, 4, 5])
    >>> cupy.pad(a, (2, 3), 'constant', constant_values=(4, 6))
    array([4, 4, 1, ..., 6, 6, 6])

    >>> cupy.pad(a, (2, 3), 'edge')
    array([1, 1, 1, ..., 5, 5, 5])

    >>> cupy.pad(a, (2, 3), 'linear_ramp', end_values=(5, -4))
    array([ 5,  3,  1,  2,  3,  4,  5,  2, -1, -4])

    >>> cupy.pad(a, (2,), 'maximum')
    array([5, 5, 1, 2, 3, 4, 5, 5, 5])

    >>> cupy.pad(a, (2,), 'mean')
    array([3, 3, 1, 2, 3, 4, 5, 3, 3])

    >>> a = cupy.array([[1, 2], [3, 4]])
    >>> cupy.pad(a, ((3, 2), (2, 3)), 'minimum')
    array([[1, 1, 1, 2, 1, 1, 1],
           [1, 1, 1, 2, 1, 1, 1],
           [1, 1, 1, 2, 1, 1, 1],
           [1, 1, 1, 2, 1, 1, 1],
           [3, 3, 3, 4, 3, 3, 3],
           [1, 1, 1, 2, 1, 1, 1],
           [1, 1, 1, 2, 1, 1, 1]])

    >>> a = cupy.array([1, 2, 3, 4, 5])
    >>> cupy.pad(a, (2, 3), 'reflect')
    array([3, 2, 1, 2, 3, 4, 5, 4, 3, 2])

    >>> cupy.pad(a, (2, 3), 'reflect', reflect_type='odd')
    array([-1,  0,  1,  2,  3,  4,  5,  6,  7,  8])

    >>> cupy.pad(a, (2, 3), 'symmetric')
    array([2, 1, 1, 2, 3, 4, 5, 5, 4, 3])

    >>> cupy.pad(a, (2, 3), 'symmetric', reflect_type='odd')
    array([0, 1, 1, 2, 3, 4, 5, 5, 6, 7])

    >>> cupy.pad(a, (2, 3), 'wrap')
    array([4, 5, 1, 2, 3, 4, 5, 1, 2, 3])

    >>> def pad_with(vector, pad_width, iaxis, kwargs):
    ...     pad_value = kwargs.get('padder', 10)
    ...     vector[:pad_width[0]] = pad_value
    ...     vector[-pad_width[1]:] = pad_value
    >>> a = cupy.arange(6)
    >>> a = a.reshape((2, 3))
    >>> cupy.pad(a, 2, pad_with)
    array([[10, 10, 10, 10, 10, 10, 10],
           [10, 10, 10, 10, 10, 10, 10],
           [10, 10,  0,  1,  2, 10, 10],
           [10, 10,  3,  4,  5, 10, 10],
           [10, 10, 10, 10, 10, 10, 10],
           [10, 10, 10, 10, 10, 10, 10]])
    >>> cupy.pad(a, 2, pad_with, padder=100)
    array([[100, 100, 100, 100, 100, 100, 100],
           [100, 100, 100, 100, 100, 100, 100],
           [100, 100,   0,   1,   2, 100, 100],
           [100, 100,   3,   4,   5, 100, 100],
           [100, 100, 100, 100, 100, 100, 100],
           [100, 100, 100, 100, 100, 100, 100]])
    """
    pad_width = numpy.asarray(pad_width)

    if not pad_width.dtype.kind == 'i':
        raise TypeError('`pad_width` must be of integral type.')

    # Broadcast to shape (array.ndim, 2)
    pad_width = _as_pairs(pad_width, array.ndim, as_index=True)

    if callable(mode):
        # Old behavior: Use user-supplied function with numpy.apply_along_axis
        function = mode
        # Create a new zero padded array
        padded, _ = _pad_simple(array, pad_width, fill_value=0)
        # And apply along each axis

        for axis in range(padded.ndim):
            # Iterate using ndindex as in apply_along_axis, but assuming that
            # function operates inplace on the padded array.

            # view with the iteration axis at the end
            view = cupy.moveaxis(padded, axis, -1)

            # compute indices for the iteration axes, and append a trailing
            # ellipsis to prevent 0d arrays decaying to scalars (gh-8642)
            inds = numpy.ndindex(view.shape[:-1])
            inds = (ind + (Ellipsis,) for ind in inds)
            for ind in inds:
                function(view[ind], pad_width[axis], axis, kwargs)

        return padded

    # Make sure that no unsupported keywords were passed for the current mode
    allowed_kwargs = {
        'empty': [],
        'edge': [],
        'wrap': [],
        'constant': ['constant_values'],
        'linear_ramp': ['end_values'],
        'maximum': ['stat_length'],
        'mean': ['stat_length'],
        # 'median': ['stat_length'],
        'minimum': ['stat_length'],
        'reflect': ['reflect_type'],
        'symmetric': ['reflect_type'],
    }
    try:
        unsupported_kwargs = set(kwargs) - set(allowed_kwargs[mode])
    except KeyError:
        raise ValueError("mode '{}' is not supported".format(mode))
    if unsupported_kwargs:
        raise ValueError(
            "unsupported keyword arguments for mode '{}': {}".format(
                mode, unsupported_kwargs
            )
        )

    stat_functions = {
        'maximum': cupy.max,
        'minimum': cupy.min,
        'mean': cupy.mean,
        # 'median': cupy.median,
    }

    # Create array with final shape and original values
    # (padded area is undefined)
    padded, original_area_slice = _pad_simple(array, pad_width)
    # And prepare iteration over all dimensions
    # (zipping may be more readable than using enumerate)
    axes = range(padded.ndim)

    if mode == 'constant':
        values = kwargs.get('constant_values', 0)
        values = _as_pairs(values, padded.ndim)
        for axis, width_pair, value_pair in zip(axes, pad_width, values):
            roi = _view_roi(padded, original_area_slice, axis)
            _set_pad_area(roi, axis, width_pair, value_pair)

    elif mode == 'empty':
        pass  # Do nothing as _pad_simple already returned the correct result

    elif array.size == 0:
        # Only modes 'constant' and 'empty' can extend empty axes, all other
        # modes depend on `array` not being empty
        # -> ensure every empty axis is only 'padded with 0'
        for axis, width_pair in zip(axes, pad_width):
            if array.shape[axis] == 0 and any(width_pair):
                raise ValueError(
                    "can't extend empty axis {} using modes other than "
                    "'constant' or 'empty'".format(axis)
                )
        # passed, don't need to do anything more as _pad_simple already
        # returned the correct result

    elif mode == 'edge':
        for axis, width_pair in zip(axes, pad_width):
            roi = _view_roi(padded, original_area_slice, axis)
            edge_pair = _get_edges(roi, axis, width_pair)
            _set_pad_area(roi, axis, width_pair, edge_pair)

    elif mode == 'linear_ramp':
        end_values = kwargs.get('end_values', 0)
        end_values = _as_pairs(end_values, padded.ndim)
        for axis, width_pair, value_pair in zip(axes, pad_width, end_values):
            roi = _view_roi(padded, original_area_slice, axis)
            ramp_pair = _get_linear_ramps(roi, axis, width_pair, value_pair)
            _set_pad_area(roi, axis, width_pair, ramp_pair)

    elif mode in stat_functions:
        func = stat_functions[mode]
        length = kwargs.get('stat_length', None)
        length = _as_pairs(length, padded.ndim, as_index=True)
        for axis, width_pair, length_pair in zip(axes, pad_width, length):
            roi = _view_roi(padded, original_area_slice, axis)
            stat_pair = _get_stats(roi, axis, width_pair, length_pair, func)
            _set_pad_area(roi, axis, width_pair, stat_pair)

    elif mode in {'reflect', 'symmetric'}:
        method = kwargs.get('reflect_type', 'even')
        include_edge = True if mode == 'symmetric' else False
        for axis, (left_index, right_index) in zip(axes, pad_width):
            if array.shape[axis] == 1 and (left_index > 0 or right_index > 0):
                # Extending singleton dimension for 'reflect' is legacy
                # behavior; it really should raise an error.
                edge_pair = _get_edges(padded, axis, (left_index, right_index))
                _set_pad_area(
                    padded, axis, (left_index, right_index), edge_pair
                )
                continue

            roi = _view_roi(padded, original_area_slice, axis)
            while left_index > 0 or right_index > 0:
                # Iteratively pad until dimension is filled with reflected
                # values. This is necessary if the pad area is larger than
                # the length of the original values in the current dimension.
                left_index, right_index = _set_reflect_both(
                    roi, axis, (left_index, right_index), method, include_edge
                )

    elif mode == 'wrap':
        for axis, (left_index, right_index) in zip(axes, pad_width):
            roi = _view_roi(padded, original_area_slice, axis)
            while left_index > 0 or right_index > 0:
                # Iteratively pad until dimension is filled with wrapped
                # values. This is necessary if the pad area is larger than
                # the length of the original values in the current dimension.
                left_index, right_index = _set_wrap_both(
                    roi, axis, (left_index, right_index)
                )

    return padded
コード例 #25
0
ファイル: ranges.py プロジェクト: viantirreau/cupy
def linspace(start,
             stop,
             num=50,
             endpoint=True,
             retstep=False,
             dtype=None,
             axis=0):
    """Returns an array with evenly-spaced values within a given interval.

    Instead of specifying the step width like :func:`cupy.arange`, this
    function requires the total number of elements specified.

    Args:
        start (scalar or array_like): Starting value(s) of the sequence.
        stop (scalar or array_like): Ending value(s) of the sequence, unless
            ``endpoint`` is set to ``False``. In that case, the sequence
            consists of all but the last of ``num + 1`` evenly spaced samples,
            so that ``stop`` is excluded.  Note that the step size changes when
            ``endpoint`` is ``False``.
        num: Number of elements.
        endpoint (bool): If ``True``, the stop value is included as the last
            element. Otherwise, the stop value is omitted.
        retstep (bool): If ``True``, this function returns (array, step).
            Otherwise, it returns only the array.
        dtype: Data type specifier. It is inferred from the start and stop
            arguments by default.
        axis (int):  The axis in the result to store the samples.  Relevant
            only if start or stop are array-like.  By default ``0``, the
            samples will be along a new axis inserted at the beginning.
            Use ``-1`` to get an axis at the end.

    Returns:
        cupy.ndarray: The 1-D array of ranged values.

    .. seealso:: :func:`numpy.linspace`

    """
    if num < 0:
        raise ValueError('linspace with num<0 is not supported')
    div = (num - 1) if endpoint else num

    scalar_start = cupy.isscalar(start)
    scalar_stop = cupy.isscalar(stop)
    if scalar_start and scalar_stop:
        return _linspace_scalar(start, stop, num, endpoint, retstep, dtype)

    if not scalar_start:
        if not (isinstance(start, cupy.ndarray) and start.dtype.kind == 'f'):
            start = cupy.asarray(start) * 1.0

    if not scalar_stop:
        if not (isinstance(stop, cupy.ndarray) and stop.dtype.kind == 'f'):
            stop = cupy.asarray(stop) * 1.0

    dt = cupy.result_type(start, stop, float(num))
    if dtype is None:
        # In actual implementation, only float is used
        dtype = dt

    delta = stop - start

    # ret = cupy.arange(0, num, dtype=dt).reshape((-1,) + (1,) * delta.ndim)
    ret = cupy.empty((num, ), dtype=dt)
    _arange_ufunc(0.0, 1.0, ret, dtype=dt)
    ret = ret.reshape((-1, ) + (1, ) * delta.ndim)

    # In-place multiplication y *= delta/div is faster, but prevents the
    # multiplicant from overriding what class is produced, and thus prevents,
    # e.g. use of Quantities, see numpy#7142. Hence, we multiply in place only
    # for standard scalar types.
    if num > 1:
        step = delta / div
        if cupy.any(step == 0):
            # Special handling for denormal numbers, numpy#5437
            ret /= div
            ret = ret * delta
        else:
            ret = ret * step
    else:
        # 0 and 1 item long sequences have an undefined step
        step = float('nan')
        # Multiply with delta to allow possible override of output class.
        ret = ret * delta

    ret += start
    if endpoint and num > 1:
        ret[-1] = stop

    if axis != 0:
        ret = cupy.moveaxis(ret, 0, axis)

    if cupy.issubdtype(dtype, cupy.integer):
        cupy.floor(ret, out=ret)

    ret = ret.astype(dtype, copy=False)

    if retstep:
        return ret, step
    else:
        return ret
コード例 #26
0
 def test_moveaxis_invalid5_3(self):
     a = testing.shaped_arange((2, 3, 4), cupy)
     with self.assertRaises(numpy.AxisError):
         return cupy.moveaxis(a, [0, 1], [1, 1])