示例#1
0
def gaussian_filter(input, sigma, order=0, output=None,
                  mode="reflect", cval=0.0, truncate=4.0):
    """Multidimensional Gaussian filter.

    Parameters
    ----------
    %(input)s
    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 : {0, 1, 2, 3} or sequence from same set, 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. An order of 1, 2, or 3
        corresponds to convolution with the first, second or third
        derivatives of a Gaussian. Higher order derivatives are not
        implemented
    %(output)s
    %(mode)s
    %(cval)s
    truncate : float
        Truncate the filter at this many standard deviations.
        Default is 4.0.

    Returns
    -------
    gaussian_filter : 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 = numpy.asarray(input)
    output, return_value = _ni_support._get_output(output, input)
    orders = _ni_support._normalize_sequence(order, input.ndim)
    if not set(orders).issubset(set(range(4))):
        raise ValueError('Order outside 0..4 not implemented')
    sigmas = _ni_support._normalize_sequence(sigma, input.ndim)
    axes = list(range(input.ndim))
    axes = [(axes[ii], sigmas[ii], orders[ii])
                        for ii in range(len(axes)) if sigmas[ii] > 1e-15]
    if len(axes) > 0:
        for axis, sigma, order in axes:
            gaussian_filter1d(input, sigma, axis, order, output,
                              mode, cval, truncate)
            input = output
    else:
        output[...] = input[...]
    return return_value
def rolling_ball_filter(data, ball_radius, spacing=None, top=False, **kwargs):
    """Rolling ball filter implemented with morphology operations

    This implenetation is very similar to that in ImageJ and uses a top hat transform
    with a ball shaped structuring element
    https://en.wikipedia.org/wiki/Top-hat_transform

    Parameters
    ----------
    data : ndarray
        image data (assumed to be on a regular grid)
    ball_radius : float
        the radius of the ball to roll
    spacing : int or sequence
        the spacing of the image data
    top : bool
        whether to roll the ball on the top or bottom of the data
    kwargs : key word arguments
        these are passed to the ndimage morphological operations

    Returns
    -------
    data_nb : ndarray
        data with background subtracted
    bg : ndarray
        background that was subtracted from the data
    """
    ndim = data.ndim
    if spacing is None:
        spacing = np.ones_like(ndim)
    else:
        spacing = _normalize_sequence(spacing, ndim)

    radius = np.asarray(_normalize_sequence(ball_radius, ndim))
    mesh = np.array(
        np.meshgrid(
            *[np.arange(-r, r + s, s) for r, s in zip(radius, spacing)],
            indexing="ij"))
    structure = 2 * np.sqrt(1 - ((mesh / radius.reshape(-1, *(
        (1, ) * ndim)))**2).sum(0))
    structure[~np.isfinite(structure)] = 0
    if not top:
        # ndi.white_tophat(y, structure=structure, output=background)
        background = ndi.grey_erosion(data, structure=structure, **kwargs)
        background = ndi.grey_dilation(background,
                                       structure=structure,
                                       **kwargs)
    else:
        # ndi.black_tophat(y, structure=structure, output=background)
        background = ndi.grey_dilation(data, structure=structure, **kwargs)
        background = ndi.grey_erosion(background,
                                      structure=structure,
                                      **kwargs)

    return data - background, background
def rolling_ball_filter(data, ball_radius, spacing=None, top=False, **kwargs):
    """Rolling ball filter implemented with morphology operations

    This implenetation is very similar to that in ImageJ and uses a top hat transform
    with a ball shaped structuring element
    https://en.wikipedia.org/wiki/Top-hat_transform

    Parameters
    ----------
    data : ndarray type uint8
        image data (assumed to be on a regular grid)
    ball_radius : float
        the radius of the ball to roll
    spacing : int or sequence
        the spacing of the image data
    top : bool
        whether to roll the ball on the top or bottom of the data
    kwargs : key word arguments
        these are passed to the ndimage morphological operations

    Returns
    -------
    data_nb : ndarray
        data with background subtracted as uint8
    bg : ndarray
        background that was subtracted from the data
    """
    data = data.astype(np.int16)
    ndim = data.ndim
    if spacing is None:
        spacing = _normalize_sequence(1, ndim)
    else:
        spacing = _normalize_sequence(spacing, ndim)

    radius = np.asarray(_normalize_sequence(ball_radius, ndim))
    mesh = np.array(np.meshgrid(*[np.arange(-r, r + s, s) for r, s in zip(radius, spacing)], indexing="ij"))
    structure = 2 * np.sqrt(2 - ((mesh / radius.reshape(-1, *((1,) * ndim)))**2).sum(0))
    structure[~np.isfinite(structure)] = 0
    if not top:
        # ndi.white_tophat(y, structure=structure, output=background)
        background = ndi.grey_erosion(data, structure=structure, **kwargs)
        background = ndi.grey_dilation(background, structure=structure, **kwargs)
    else:
        # ndi.black_tophat(y, structure=structure, output=background)
        background = ndi.grey_dilation(data, structure=structure, **kwargs)
        background = ndi.grey_erosion(background, structure=structure, **kwargs)

    data_corr = data - background
    data_corr[data_corr<0] = 0

    return data_corr.astype(np.uint8), background.astype(np.uint8)
示例#4
0
文件: binary.py 项目: shihao80/medpy
def __surface_distances(result, reference, voxelspacing=None, connectivity=1):
    """
    The distances between the surface voxel of binary objects in result and their
    nearest partner surface voxel of a binary object in reference.
    """
    result = numpy.atleast_1d(result.astype(numpy.bool))
    reference = numpy.atleast_1d(reference.astype(numpy.bool))
    if voxelspacing is not None:
        voxelspacing = _ni_support._normalize_sequence(voxelspacing, result.ndim)
        voxelspacing = numpy.asarray(voxelspacing, dtype=numpy.float64)
        if not voxelspacing.flags.contiguous:
            voxelspacing = voxelspacing.copy()
            
    # binary structure
    footprint = generate_binary_structure(result.ndim, connectivity)
    
    # test for emptiness
    if 0 == numpy.count_nonzero(result): 
        raise RuntimeError('The first supplied array does not contain any binary object.')
    if 0 == numpy.count_nonzero(reference): 
        raise RuntimeError('The second supplied array does not contain any binary object.')    
            
    # extract only 1-pixel border line of objects
    result_border = result ^ binary_erosion(result, structure=footprint, iterations=1)
    reference_border = reference ^ binary_erosion(reference, structure=footprint, iterations=1)
    
    # compute average surface distance        
    # Note: scipys distance transform is calculated only inside the borders of the
    #       foreground objects, therefore the input has to be reversed
    dt = distance_transform_edt(~reference_border, sampling=voxelspacing)
    sds = dt[result_border]
    
    return sds
示例#5
0
文件: binary.py 项目: kleinfeld/medpy
def __surface_distances(input1, input2, voxelspacing=None, connectivity=1):
    """
    The distances between the surface voxel of binary objects in input1 and their
    nearest partner surface voxel of a binary object in input2.
    """
    input1 = numpy.atleast_1d(input1.astype(numpy.bool))
    input2 = numpy.atleast_1d(input2.astype(numpy.bool))
    if voxelspacing is not None:
        voxelspacing = _ni_support._normalize_sequence(voxelspacing, input1.ndim)
        voxelspacing = numpy.asarray(voxelspacing, dtype=numpy.float64)
        if not voxelspacing.flags.contiguous:
            voxelspacing = voxelspacing.copy()
            
    # binary structure
    footprint = generate_binary_structure(input1.ndim, connectivity)
            
    # extract only 1-pixel border line of objects
    input1_border = input1 - binary_erosion(input1, structure=footprint, iterations=1)
    input2_border = input2 - binary_erosion(input2, structure=footprint, iterations=1)
    
    # compute average surface distance        
    # Note: scipys distance transform is calculated only inside the borders of the
    #       foreground objects, therefore the input has to be reversed
    dt = distance_transform_edt(~input2_border, sampling=voxelspacing)
    sds = dt[input1_border]
    
    return sds    
示例#6
0
def _surface_distances(input1, input2, voxelspacing=None, connectivity=1):
    input1 = np.atleast_1d(input1.astype(bool))
    input2 = np.atleast_1d(input2.astype(bool))
    if voxelspacing is not None:
        voxelspacing = _ni_support._normalize_sequence(voxelspacing,
                                                       input1.ndim)
        voxelspacing = np.asarray(voxelspacing, dtype=np.float64)
        if not voxelspacing.flags.contiguous:
            voxelspacing = voxelspacing.copy()

    if 0 == np.count_nonzero(input1):
        raise RuntimeError(
            'The first supplied array does not contain any binary object.')
    if 0 == np.count_nonzero(input2):
        raise RuntimeError(
            'The second supplied array does not contain any binary object.')

    # binary structure, used by binary_erosion()
    footprint = generate_binary_structure(input1.ndim, connectivity)
    # extract only 1-pixel border line of objects
    input1_border = input1 - binary_erosion(
        input1, structure=footprint, iterations=1)
    input2_border = input2 - binary_erosion(
        input2, structure=footprint, iterations=1)

    # compute average surface distance
    # Note: scipy's distance transform is calculated only inside the borders of the
    #       foreground objects, therefore the input has to be reversed
    dt = distance_transform_edt(~input2_border, sampling=voxelspacing)
    sds = dt[input1_border]

    return sds
示例#7
0
def square_gaussian_filter(input, sigma, output = None, mode = "reflect", cval = 0.0):
    """Multi-dimensional Squared Gaussian filter.

    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.

    Note: The multi-dimensional 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 = np.asarray(input)
    output, return_value =_ni_support._get_output(output, input)
    sigmas =_ni_support._normalize_sequence(sigma, input.ndim)
    axes = range(input.ndim)
    axes = [(axes[ii], sigmas[ii])
                        for ii in range(len(axes)) if sigmas[ii] > 1e-15]
    if len(axes) > 0:
        for axis, sigma in axes:
            square_gaussian_filter1d(input, sigma, axis, output,
                              mode, cval)
            input = output
    else:
        output[...] = input[...]
    return return_value
示例#8
0
def square_gaussian_filter(input,
                           sigma,
                           output=None,
                           mode="reflect",
                           cval=0.0):
    """Multi-dimensional Squared Gaussian filter.

    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.

    Note: The multi-dimensional 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 = np.asarray(input)
    output, return_value = _ni_support._get_output(output, input)
    sigmas = _ni_support._normalize_sequence(sigma, input.ndim)
    axes = range(input.ndim)
    axes = [(axes[ii], sigmas[ii]) for ii in range(len(axes))
            if sigmas[ii] > 1e-15]
    if len(axes) > 0:
        for axis, sigma in axes:
            square_gaussian_filter1d(input, sigma, axis, output, mode, cval)
            input = output
    else:
        output[...] = input[...]
    return return_value
示例#9
0
文件: binary.py 项目: potis/medpy
def __surface_distances(result, reference, voxelspacing=None, connectivity=1):
    """
    The distances between the surface voxel of binary objects in result and their
    nearest partner surface voxel of a binary object in reference.
    """
    result = numpy.atleast_1d(result.astype(numpy.bool))
    reference = numpy.atleast_1d(reference.astype(numpy.bool))
    if voxelspacing is not None:
        voxelspacing = _ni_support._normalize_sequence(voxelspacing, result.ndim)
        voxelspacing = numpy.asarray(voxelspacing, dtype=numpy.float64)
        if not voxelspacing.flags.contiguous:
            voxelspacing = voxelspacing.copy()
            
    # binary structure
    footprint = generate_binary_structure(result.ndim, connectivity)
    
    # test for emptiness
    if 0 == numpy.count_nonzero(result): 
        raise RuntimeError('The first supplied array does not contain any binary object.')
    if 0 == numpy.count_nonzero(reference): 
        raise RuntimeError('The second supplied array does not contain any binary object.')    
            
    # extract only 1-pixel border line of objects
    result_border = result - binary_erosion(result, structure=footprint, iterations=1)
    reference_border = reference - binary_erosion(reference, structure=footprint, iterations=1)
    
    # compute average surface distance        
    # Note: scipys distance transform is calculated only inside the borders of the
    #       foreground objects, therefore the input has to be reversed
    dt = distance_transform_edt(~reference_border, sampling=voxelspacing)
    sds = dt[result_border]
    
    return sds
示例#10
0
def _correlate_or_convolve(input, weights, output, mode, cval, origin,
                           convolution):
    input = np.asarray(input)
    if np.iscomplexobj(input):
        raise TypeError('Complex type not supported')
    origins = _ni_support._normalize_sequence(origin, input.ndim)
    weights = np.asarray(weights, dtype=np.float64)
    wshape = [ii for ii in weights.shape if ii > 0]
    if len(wshape) != input.ndim:
        raise RuntimeError('filter weights array has incorrect shape.')
    if convolution:
        weights = weights[tuple([slice(None, None, -1)] * weights.ndim)]
        for ii in range(len(origins)):
            origins[ii] = -origins[ii]
            if not weights.shape[ii] & 1:
                origins[ii] -= 1
    for origin, lenw in zip(origins, wshape):
        if _invalid_origin(origin, lenw):
            raise ValueError('Invalid origin; origin must satisfy '
                             '-(weights.shape[k] // 2) <= origin[k] <= '
                             '(weights.shape[k]-1) // 2')

    if not weights.flags.contiguous:
        weights = weights.copy()
    output = _ni_support._get_output(output, input)
    mode = _ni_support._extend_mode_to_code(mode)
    _nd_image.correlate(input, weights, output, mode, cval, origins)
    return output
示例#11
0
def sinc_filter(input, sigma, order=0, output=None,
                    mode="reflect", cval=0.0, truncate=6.0):
    input = np.asarray(input)
    output = _ni_support._get_output(output, input)
    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:
        for axis, sigma, order, mode in axes:
            sinc_filter1d(input, sigma, axis, order, output,
                              mode, cval, truncate)
            input = output
    else:
        output[...] = input[...]
    return output
示例#12
0
def __make_footprint(input, size, footprint):
    "Creates a standard footprint element ala scipy.ndimage."
    if footprint is None:
        if size is None:
            raise RuntimeError("no footprint or filter size provided")
        sizes = _ni_support._normalize_sequence(size, input.ndim)
        footprint = numpy.ones(sizes, dtype=bool)
    else:
        footprint = numpy.asarray(footprint, dtype=bool)
    return footprint
示例#13
0
def __make_footprint(input, size, footprint):
    "Creates a standard footprint element ala scipy.ndimage."
    if footprint is None:
        if size is None:
            raise RuntimeError("no footprint or filter size provided")
        sizes = _ni_support._normalize_sequence(size, input.ndim)
        footprint = numpy.ones(sizes, dtype=bool)
    else:
        footprint = numpy.asarray(footprint, dtype=bool)
    return footprint
示例#14
0
def slice_maker(xs, ws):
    """
    A utility function to generate slices for later use.

    Parameters
    ----------
    y0 : int
        center y position of the slice
    x0 : int
        center x position of the slice
    width : int
        Width of the slice

    Returns
    -------
    slices : list
        A list of slice objects, the first one is for the y dimension and
        and the second is for the x dimension.

    Notes
    -----
    The method will automatically coerce slices into acceptable bounds.

    Examples
    --------
    >>> slice_maker((30,20),10)
    [slice(25, 35, None), slice(15, 25, None)]
    >>> slice_maker((30,20),25)
    [slice(18, 43, None), slice(8, 33, None)]
    """
    # normalize inputs
    xs = np.asarray(xs)
    ws = np.asarray(_normalize_sequence(ws, len(xs)))
    if not np.isrealobj((xs, ws)):
        raise TypeError("`slice_maker` only accepts real input")
    if np.any(ws < 0):
        raise ValueError("width cannot be negative, width = {}".format(ws))
    # ensure integers
    xs = np.rint(xs).astype(int)
    ws = np.rint(ws).astype(int)
    # use _calc_pad
    toreturn = []
    for x, w in zip(xs, ws):
        half2, half1 = _calc_pad(0, w)
        xstart = x - half1
        xend = x + half2
        assert xstart <= xend, "xstart > xend"
        if xend <= 0:
            xstart, xend = 0, 0
        # the max calls are to make slice_maker play nice with edges.
        toreturn.append(slice(max(0, xstart), xend))
    # return a list of slices
    return tuple(toreturn)
示例#15
0
def gaussian_filter(array: np.ndarray,
                    sigma: float,
                    order: int = 0,
                    mode='constant',
                    cval: float = 0.0,
                    truncate: float = 4.0,
                    radius: int = None,
                    points: np.ndarray = None,
                    nonzero: bool = False):
    orders = _ni_support._normalize_sequence(order, array.ndim)
    sigmas = _ni_support._normalize_sequence(sigma, array.ndim)
    modes = _ni_support._normalize_sequence(mode, array.ndim)
    axes = list(range(array.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:
        for axis, sigma, order, mode in axes:
            array = gaussian_filter1d(array, sigma, axis, order, mode, cval,
                                      truncate, radius, points, nonzero)

    return array
    def rolling_ball_filter(self,
                            data,
                            ball_radius,
                            spacing=None,
                            top=False,
                            **kwargs):
        data = data.astype(np.int16)
        ndim = data.ndim
        if spacing is None:
            spacing = _normalize_sequence(1, ndim)
        else:
            spacing = _normalize_sequence(spacing, ndim)

        radius = np.asarray(_normalize_sequence(ball_radius, ndim))
        mesh = np.array(
            np.meshgrid(
                *[np.arange(-r, r + s, s) for r, s in zip(radius, spacing)],
                indexing="ij"))
        structure = np.uint8(2 * np.sqrt(
            np.absolute(2 - ((mesh / radius.reshape(-1, *(
                (1, ) * ndim)))**2).sum(0))))
        structure[~np.isfinite(structure)] = 0

        if not top:
            # ndi.white_tophat(y, structure=structure, output=background)
            background = cv.erode(data, structure, **kwargs)
            background = cv.dilate(background, structure, **kwargs)
        else:
            # ndi.black_tophat(y, structure=structure, output=background)
            background = cv.dilate(data, structure, **kwargs)
            background = cv.erode(background, structure, **kwargs)

        data_corr = data - background
        data_corr[data_corr < 0] = 0

        return data_corr.astype(np.uint8), background.astype(np.uint8)
示例#17
0
def __surface_distances(result, reference, voxelspacing=None, connectivity=1):
    """
    The distances between the surface voxel of binary objects in result and their
    nearest partner surface voxel of a binary object in reference.

    Adopted from https://github.com/loli/medpy/blob/39131b94f0ab5328ab14a874229320efc2f74d98/medpy/metric/binary.py#L1195
    """
    result = numpy.atleast_1d(result.astype(numpy.bool))
    reference = numpy.atleast_1d(reference.astype(numpy.bool))
    if voxelspacing is not None:
        voxelspacing = _ni_support._normalize_sequence(voxelspacing,
                                                       result.ndim)
        voxelspacing = numpy.asarray(voxelspacing, dtype=numpy.float64)
        if not voxelspacing.flags.contiguous:
            voxelspacing = voxelspacing.copy()

    # binary structure
    footprint = generate_binary_structure(result.ndim, connectivity)

    # test for emptiness
    if 0 == numpy.count_nonzero(result):
        # print(
        #     "The first supplied array does not contain any binary object.",
        #     file=sys.stderr,
        # )
        return 0
    if 0 == numpy.count_nonzero(reference):
        # print(
        #     "The second supplied array does not contain any binary object.",
        #     file=sys.stderr,
        # )
        return 0

    # extract only 1-pixel border line of objects
    result_border = result ^ binary_erosion(
        result, structure=footprint, iterations=1)
    reference_border = reference ^ binary_erosion(
        reference, structure=footprint, iterations=1)

    # compute average surface distance
    # Note: scipys distance transform is calculated only inside the borders of the
    #       foreground objects, therefore the input has to be reversed
    dt = distance_transform_edt(~reference_border, sampling=voxelspacing)
    sds = dt[result_border]

    return sds
示例#18
0
def surface_distances(input1, input2, voxelspacing=None, connectivity=1.0):
    """
    http://pythonhosted.org/MedPy/_modules/medpy/metric/binary.html
    The distances between the surface voxel of binary objects in input1 and their
    nearest partner surface voxel of a binary object in input2.
    """
    input1 = np.atleast_1d(input1.astype(np.bool))
    input2 = np.atleast_1d(input2.astype(np.bool))
    if voxelspacing is not None:
        voxelspacing = _ni_support._normalize_sequence(voxelspacing,
                                                       input1.ndim)
        voxelspacing = np.asarray(voxelspacing, dtype=np.float64)
        if not voxelspacing.flags.contiguous:
            voxelspacing = voxelspacing.copy()

    # binary structure
    footprint = generate_binary_structure(input1.ndim, connectivity)

    # test for emptiness
    if 0 == np.count_nonzero(input1):
        return np.array([
            float("inf")
        ])  # Can´t calculate the hausdorff distance if there is no object
    if 0 == np.count_nonzero(input2):
        return np.array([
            float("inf")
        ])  # Can´t calculate the hausdorff distance if there is no object

        # extract only 1-pixel border line of objects
    input1_border = input1 ^ binary_erosion(
        input1, structure=footprint, iterations=1)
    input2_border = input2 ^ binary_erosion(
        input2, structure=footprint, iterations=1)

    # compute average surface distance
    # Note: scipys distance transform is calculated only inside the borders of the
    #       foreground objects, therefore the input has to be reversed
    dt = distance_transform_edt(~input2_border, sampling=voxelspacing)
    sds = dt[input1_border]

    return sds
示例#19
0
def __surface_distances(input1, input2, voxelspacing=None, connectivity=1):
    """
    The distances between the surface voxel of binary objects in input1 and their
    nearest partner surface voxel of a binary object in input2.
    """
    input1 = numpy.atleast_1d(input1.astype(numpy.bool))
    input2 = numpy.atleast_1d(input2.astype(numpy.bool))
    if voxelspacing is not None:
        voxelspacing = _ni_support._normalize_sequence(voxelspacing,
                                                       input1.ndim)
        voxelspacing = numpy.asarray(voxelspacing, dtype=numpy.float64)
        if not voxelspacing.flags.contiguous:
            voxelspacing = voxelspacing.copy()

    # binary structure
    footprint = generate_binary_structure(input1.ndim, connectivity)

    # test for emptiness
    # if 0 == numpy.count_nonzero(input1):
    #     raise RuntimeError('The first supplied array does not contain any binary object.')
    # if 0 == numpy.count_nonzero(input2):
    #     raise RuntimeError('The second supplied array does not contain any binary object.')

    if numpy.count_nonzero(input1) == 0 or numpy.count_nonzero(input2) == 0:
        return numpy.max(numpy.shape(input1))
    # extract only 1-pixel border line of objects
    input1_border = input1 - binary_erosion(
        input1, structure=footprint, iterations=1)
    input2_border = input2 - binary_erosion(
        input2, structure=footprint, iterations=1)

    # compute average surface distance
    # Note: scipys distance transform is calculated only inside the borders of the
    #       foreground objects, therefore the input has to be reversed
    dt = distance_transform_edt(~input2_border, sampling=voxelspacing)
    sds = dt[input1_border]

    return sds
示例#20
0
def smo(input, sigma, size):
    """Applies the Silver Mountain Operator (SMO) to a scalar field.

    Parameters
    ----------
    input : numpy.array
        Input field.
    sigma : scalar or sequence of scalars
        Standard deviation for Gaussian kernel.
    size : int or sequence of int
        Averaging window size.

    Returns
    -------
    numpy.array
    """
    size = _normalize_sequence(size, input.ndim)
    input = ndimage.gaussian_filter(input.astype(float, copy=False),
                                    sigma=sigma)
    norm_grad = normalized_gradient(input)
    sliding_mean = ndimage.uniform_filter(norm_grad, size=(1, *size))
    magnitude = np.linalg.norm(sliding_mean, axis=0)
    return magnitude
示例#21
0
def pad(input, size=None, footprint=None, output=None, mode="reflect", cval=0.0):
    """
    Returns a copy of the input, padded by the supplied structuring element.
    
    In the case of odd dimensionality, the structure element will be centered as
    following on the currently processed position:
    [[T, Tx, T],
     [T, T , T]]
    , where Tx denotes the center of the structure element.

    Simulates the behaviour of scipy.ndimage filters.

    input : array_like
        Input array to pad.
    size : scalar or tuple, optional
        See footprint, below
    footprint : array, optional
        Either `size` or `footprint` must be defined. `size` gives
        the shape that is taken from the input array, at every element
        position, to define the input to the filter function.
        `footprint` is a boolean array that specifies (implicitly) a
        shape, but also which of the elements within this shape will get
        passed to the filter function. Thus ``size=(n,m)`` is equivalent
        to ``footprint=np.ones((n,m))``. We adjust `size` to the number
        of dimensions of the input array, so that, if the input array is
        shape (10,10,10), and `size` is 2, then the actual size used is
        (2,2,2).
    output : array, optional
        The `output` parameter passes an array in which to store the
        filter output.
    mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional
        The `mode` parameter determines how the array borders are
        handled, where `cval` is the value when mode is equal to
        'constant'. Default is 'reflect'.
    cval : scalar, optional
        Value to fill past edges of input if `mode` is 'constant'. Default
        is 0.0
    """
    input = numpy.asarray(input)
    if footprint is None:
        if size is None:
            raise RuntimeError("no footprint or filter size provided")
        sizes = _ni_support._normalize_sequence(size, input.ndim)
        footprint = numpy.ones(sizes, dtype=bool)
    else:
        footprint = numpy.asarray(footprint, dtype=bool)
    fshape = [ii for ii in footprint.shape if ii > 0]
    if len(fshape) != input.ndim:
        raise RuntimeError('filter footprint array has incorrect shape.')

    padding_offset = [((s - 1) / 2, s / 2) for s in fshape]
    input_slicer = [slice(l, None if 0 == r else -1 * r) for l, r in padding_offset]
    output_shape = [s + sum(os) for s, os in zip(input.shape, padding_offset)]
    output, return_value = _ni_support._get_output(output, input, output_shape)

    if 'constant' == mode:
        output += cval
        output[input_slicer] = input
        return return_value
    elif 'nearest' == mode:
        output[input_slicer] = input
        dim_mult_slices = [(d, l, slice(None, l), slice(l, l + 1)) for d, (l, _) in zip(range(output.ndim), padding_offset) if not 0 == l]
        dim_mult_slices.extend([(d, r, slice(-1 * r, None), slice(-2 * r, -2 * r + 1)) for d, (_, r) in zip(range(output.ndim), padding_offset) if not 0 == r])
        for dim, mult, to_slice, from_slice in dim_mult_slices:
            slicer_to = [to_slice if d == dim else slice(None) for d in range(output.ndim)]
            slicer_from = [from_slice if d == dim else slice(None) for d in range(output.ndim)]
            if not 0 == mult:
                output[slicer_to] = numpy.concatenate([output[slicer_from]] * mult, dim)
        return return_value
    elif 'mirror' == mode:
        dim_slices = [(d, slice(None, l), slice(l + 1, 2 * l + 1)) for d, (l, _) in zip(range(output.ndim), padding_offset) if not 0 == l]
        dim_slices.extend([(d, slice(-1 * r, None), slice(-2 * r - 1, -1 * r - 1)) for d, (_, r) in zip(range(output.ndim), padding_offset) if not 0 == r])
        reverse_slice = slice(None, None, -1)
    elif 'reflect' == mode:
        dim_slices = [(d, slice(None, l), slice(l, 2 * l)) for d, (l, _) in zip(range(output.ndim), padding_offset) if not 0 == l]
        dim_slices.extend([(d, slice(-1 * r, None), slice(-2 * r, -1 * r)) for d, (_, r) in zip(range(output.ndim), padding_offset) if not 0 == r])
        reverse_slice = slice(None, None, -1)
    elif 'wrap' == mode:
        dim_slices = [(d, slice(None, l), slice(-1 * (l + r), -1 * r if not 0 == r else None)) for d, (l, r) in zip(range(output.ndim), padding_offset) if not 0 == l]
        dim_slices.extend([(d, slice(-1 * r, None), slice(l, r + l)) for d, (l, r) in zip(range(output.ndim), padding_offset) if not 0 == r])
        reverse_slice = slice(None)
    else:
        raise RuntimeError('boundary mode not supported')
    
    output[input_slicer] = input
    for dim, to_slice, from_slice in dim_slices:
        slicer_reverse = [reverse_slice if d == dim else slice(None) for d in range(output.ndim)]
        slicer_to = [to_slice if d == dim else slice(None) for d in range(output.ndim)]
        slicer_from = [from_slice if d == dim else slice(None) for d in range(output.ndim)]
        output[slicer_to] = output[slicer_from][slicer_reverse]

    return return_value
示例#22
0
def distance_transform_edt_float32(
    input,
    sampling=None,
    return_distances=True,
    return_indices=False,
    distances=None,
    indices=None,
):
    """
    The same as scipy.ndimage.morphology.distance_transform_edt but
    using float32 and better memory cleaning internally.

    In addition to the distance transform, the feature transform can
    be calculated. In this case the index of the closest background
    element is returned along the first axis of the result.
    Parameters
    ----------
    input : array_like
        Input data to transform. Can be any type but will be converted
        into binary: 1 wherever input equates to True, 0 elsewhere.
    sampling : float or int, or sequence of same, optional
        Spacing of elements along each dimension. If a sequence, must be of
        length equal to the input rank; if a single number, this is used for
        all axes. If not specified, a grid spacing of unity is implied.
    return_distances : bool, optional
        Whether to return distance matrix. At least one of
        return_distances/return_indices must be True. Default is True.
    return_indices : bool, optional
        Whether to return indices matrix. Default is False.
    distances : ndarray, optional
        Used for output of distance array, must be of type float64.
    indices : ndarray, optional
        Used for output of indices, must be of type int32.
    Returns
    -------
    distance_transform_edt : ndarray or list of ndarrays
        Either distance matrix, index matrix, or a list of the two,
        depending on `return_x` flags and `distance` and `indices`
        input parameters.
    Notes
    -----
    The euclidean distance transform gives values of the euclidean
    distance::
                    n
      y_i = sqrt(sum (x[i]-b[i])**2)
                    i
    where b[i] is the background point (value 0) with the smallest
    Euclidean distance to input points x[i], and n is the
    number of dimensions.

    ---

     Copyright (C) 2003-2005 Peter J. Verveer

     Redistribution and use in source and binary forms, with or without
     modification, are permitted provided that the following conditions
     are met:

     1. Redistributions of source code must retain the above copyright
        notice, this list of conditions and the following disclaimer.

     2. Redistributions in binary form must reproduce the above
        copyright notice, this list of conditions and the following
        disclaimer in the documentation and/or other materials provided
        with the distribution.

     3. The name of the author may not be used to endorse or promote
        products derived from this software without specific prior
        written permission.

     THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
     OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
     DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    """
    from scipy.ndimage import _nd_image, _ni_support

    if (not return_distances) and (not return_indices):
        msg = "at least one of distances/indices must be specified"
        raise RuntimeError(msg)

    ft_inplace = isinstance(indices, np.ndarray)
    dt_inplace = isinstance(distances, np.ndarray)

    # calculate the feature transform
    input = np.atleast_1d(np.where(input, 1, 0).astype(np.int8))

    garbage_collect = gc.collect if input.nbytes > 100E6 else lambda: None
    garbage_collect()

    input = input.astype(np.int32)
    garbage_collect()

    if sampling is not None:
        sampling = _ni_support._normalize_sequence(sampling, input.ndim)
        sampling = np.asarray(sampling, dtype=np.float64)
        if not sampling.flags.contiguous:
            sampling = sampling.copy()

    if ft_inplace:
        ft = indices
        if ft.shape != (input.ndim,) + input.shape:
            raise RuntimeError("indices has wrong shape")
        if ft.dtype.type != np.int32:
            raise RuntimeError("indices must be of int32 type")
    else:
        ft = np.zeros((input.ndim,) + input.shape, dtype=np.int32)

    _nd_image.euclidean_feature_transform(input, sampling, ft)
    input_shape = input.shape

    del input
    garbage_collect()

    # if requested, calculate the distance transform
    if return_distances:
        # dt = ft - np.indices(input.shape, dtype=ft.dtype)
        # Paul K. Gerke: Save a lot of memory by doing the operation
        # column-wise and in-pace.

        if return_indices:
            dt = ft.copy()
        else:
            dt = ft
            del ft

        c_indices = np.indices((1,) + input_shape[1:], dtype=dt.dtype)
        for c in range(input_shape[0]):
            dt[:, c : (c + 1)] -= c_indices  # noqa: E203
            c_indices[0] += 1

        dt = dt.astype(np.float32, copy=False)
        if sampling is not None:
            for ii in range(len(sampling)):
                dt[ii, ...] *= sampling[ii]
        np.multiply(dt, dt, dt)
        if dt_inplace:
            dt = np.add.reduce(dt, axis=0)
            if distances.shape != dt.shape:
                raise RuntimeError("indices has wrong shape")
            if distances.dtype.type != np.float32:
                raise RuntimeError("indices must be of float32 type")
            np.sqrt(dt, distances)
        else:
            dt = np.add.reduce(dt, axis=0)
            dt = np.sqrt(dt)

    # construct and return the result
    result = []
    if return_distances and not dt_inplace:
        result.append(dt)
    if return_indices and not ft_inplace:
        result.append(ft)

    if len(result) == 2:
        return tuple(result)
    elif len(result) == 1:
        return result[0]
    else:
        return None
示例#23
0
def zoom(input, zoom, output=None, order=3, mode='constant', cval=0.0,
         prefilter=True):
    """
    Zoom an array.

    The array is zoomed using spline interpolation of the requested order.

    Parameters
    ----------
    input : ndarray
        The input array.
    zoom : float or sequence, optional
        The zoom factor along the axes. If a float, `zoom` is the same for each
        axis. If a sequence, `zoom` should contain one value for each axis.
    output : ndarray or dtype, optional
        The array in which to place the output, or the dtype of the returned
        array.
    order : int, optional
        The order of the spline interpolation, default is 3.
        The order has to be in the range 0-5.
    mode : str, optional
        Points outside the boundaries of the input are filled according
        to the given mode ('constant', 'nearest', 'reflect' or 'wrap').
        Default is 'constant'.
    cval : scalar, optional
        Value used for points outside the boundaries of the input if
        ``mode='constant'``. Default is 0.0
    prefilter : bool, optional
        The parameter prefilter determines if the input is pre-filtered with
        `spline_filter` before interpolation (necessary for spline
        interpolation of order > 1).  If False, it is assumed that the input is
        already filtered. Default is True.

    Returns
    -------
    return_value : ndarray or None
        The zoomed input. If `output` is given as a parameter, None is
        returned.

    """
    if order < 0 or order > 5:
        raise RuntimeError('spline order not supported')
    input = numpy.asarray(input)
    if numpy.iscomplexobj(input):
        raise TypeError('Complex type not supported')
    if input.ndim < 1:
        raise RuntimeError('input and output rank must be > 0')
    mode = _extend_mode_to_code(mode)
    if prefilter and order > 1:
        filtered = spline_filter(input, order, output = numpy.float64)
    else:
        filtered = input
    zoom = _ni_support._normalize_sequence(zoom, input.ndim)
    output_shape = tuple([int(ii * jj) for ii, jj in zip(input.shape, zoom)])

    zoom_div = numpy.array(output_shape, float)
    zoom = (numpy.array(input.shape)) / zoom_div

    # Zooming to non-finite values in unpredictable, so just choose
    # zoom factor 1 instead
    zoom[~numpy.isfinite(zoom)] = 1

    output, return_value = _ni_support._get_output(output, input,
                                                   shape=output_shape)
    zoom = numpy.asarray(zoom, dtype = numpy.float64)
    zoom = numpy.ascontiguousarray(zoom)
    _nd_image.zoom_shift(filtered, zoom, None, output, order, mode, cval)
    return return_value
示例#24
0
def pad(input,
        size=None,
        footprint=None,
        output=None,
        mode="reflect",
        cval=0.0):
    r"""
    Returns a copy of the input, padded by the supplied structuring element.
    
    In the case of odd dimensionality, the structure element will be centered as
    following on the currently processed position::
    
        [[T, Tx, T],
         [T, T , T]]
         
    , where Tx denotes the center of the structure element.

    Simulates the behaviour of scipy.ndimage filters.

    Parameters
    ----------
    input : array_like
        Input array to pad.
    size : scalar or tuple, optional
        See footprint, below
    footprint : array, optional
        Either `size` or `footprint` must be defined. `size` gives
        the shape that is taken from the input array, at every element
        position, to define the input to the filter function.
        `footprint` is a boolean array that specifies (implicitly) a
        shape, but also which of the elements within this shape will get
        passed to the filter function. Thus ``size=(n,m)`` is equivalent
        to ``footprint=np.ones((n,m))``. We adjust `size` to the number
        of dimensions of the input array, so that, if the input array is
        shape (10,10,10), and `size` is 2, then the actual size used is
        (2,2,2).
    output : array, optional
        The `output` parameter passes an array in which to store the
        filter output.
    mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional
        The `mode` parameter determines how the array borders are
        handled, where `cval` is the value when mode is equal to
        'constant'. Default is 'reflect'.
    cval : scalar, optional
        Value to fill past edges of input if `mode` is 'constant'. Default
        is 0.0
        
    Returns
    -------
    output : ndarray
        The padded version of the input image.
        
    Notes
    -----
    Since version 1.7.0, numpy supplied a pad function `numpy.pad` that provides
    the same functionality and should be preferred.
    
    Raises
    ------
    ValueError
        If the provided footprint/size is more than double the image size.
    """
    input = numpy.asarray(input)
    if footprint is None:
        if size is None:
            raise RuntimeError("no footprint or filter size provided")
        sizes = _ni_support._normalize_sequence(size, input.ndim)
        footprint = numpy.ones(sizes, dtype=bool)
    else:
        footprint = numpy.asarray(footprint, dtype=bool)
    fshape = [ii for ii in footprint.shape if ii > 0]
    if len(fshape) != input.ndim:
        raise RuntimeError('filter footprint array has incorrect shape.')

    if numpy.any([x > 2 * y for x, y in zip(footprint.shape, input.shape)]):
        raise ValueError(
            'The size of the padding element is not allowed to be more than double the size of the input array in any dimension.'
        )

    padding_offset = [((s - 1) / 2, s / 2) for s in fshape]
    input_slicer = [
        slice(l, None if 0 == r else -1 * r) for l, r in padding_offset
    ]
    output_shape = [s + sum(os) for s, os in zip(input.shape, padding_offset)]
    output, return_value = _ni_support._get_output(output, input, output_shape)

    if 'constant' == mode:
        output += cval
        output[input_slicer] = input
        return return_value
    elif 'nearest' == mode:
        output[input_slicer] = input
        dim_mult_slices = [
            (d, l, slice(None, l), slice(l, l + 1))
            for d, (l, _) in zip(list(range(output.ndim)), padding_offset)
            if not 0 == l
        ]
        dim_mult_slices.extend([
            (d, r, slice(-1 * r, None), slice(-2 * r, -2 * r + 1))
            for d, (_, r) in zip(list(range(output.ndim)), padding_offset)
            if not 0 == r
        ])
        for dim, mult, to_slice, from_slice in dim_mult_slices:
            slicer_to = [
                to_slice if d == dim else slice(None)
                for d in range(output.ndim)
            ]
            slicer_from = [
                from_slice if d == dim else slice(None)
                for d in range(output.ndim)
            ]
            if not 0 == mult:
                output[slicer_to] = numpy.concatenate([output[slicer_from]] *
                                                      mult, dim)
        return return_value
    elif 'mirror' == mode:
        dim_slices = [
            (d, slice(None, l), slice(l + 1, 2 * l + 1))
            for d, (l, _) in zip(list(range(output.ndim)), padding_offset)
            if not 0 == l
        ]
        dim_slices.extend([
            (d, slice(-1 * r, None), slice(-2 * r - 1, -1 * r - 1))
            for d, (_, r) in zip(list(range(output.ndim)), padding_offset)
            if not 0 == r
        ])
        reverse_slice = slice(None, None, -1)
    elif 'reflect' == mode:
        dim_slices = [
            (d, slice(None, l), slice(l, 2 * l))
            for d, (l, _) in zip(list(range(output.ndim)), padding_offset)
            if not 0 == l
        ]
        dim_slices.extend([
            (d, slice(-1 * r, None), slice(-2 * r, -1 * r))
            for d, (_, r) in zip(list(range(output.ndim)), padding_offset)
            if not 0 == r
        ])
        reverse_slice = slice(None, None, -1)
    elif 'wrap' == mode:
        dim_slices = [
            (d, slice(None,
                      l), slice(-1 * (l + r), -1 * r if not 0 == r else None))
            for d, (l, r) in zip(list(range(output.ndim)), padding_offset)
            if not 0 == l
        ]
        dim_slices.extend([
            (d, slice(-1 * r, None), slice(l, r + l))
            for d, (l, r) in zip(list(range(output.ndim)), padding_offset)
            if not 0 == r
        ])
        reverse_slice = slice(None)
    else:
        raise RuntimeError('boundary mode not supported')

    output[input_slicer] = input
    for dim, to_slice, from_slice in dim_slices:
        slicer_reverse = [
            reverse_slice if d == dim else slice(None)
            for d in range(output.ndim)
        ]
        slicer_to = [
            to_slice if d == dim else slice(None) for d in range(output.ndim)
        ]
        slicer_from = [
            from_slice if d == dim else slice(None) for d in range(output.ndim)
        ]
        output[slicer_to] = output[slicer_from][slicer_reverse]

    return return_value
示例#25
0
def rolling_ball_filter(data, ball_radius, spacing=None, top=False, **kwargs):
    """Filter data via a rolling ball algorithm.

    Implemented with morphological operations

    This implenetation is very similar to that in ImageJ and uses a top hat transform
    with a ball shaped structuring element
    https://en.wikipedia.org/wiki/Top-hat_transform

    Parameters
    ----------
    data : ndarray
        image data (assumed to be on a regular grid)
    ball_radius : float
        the radius of the ball to roll
    spacing : int or sequence
        the spacing of the image data
    top : bool
        whether to roll the ball on the top or bottom of the data
    kwargs : key word arguments
        these are passed to the ndimage morphological operations

    Returns
    -------
    data_nb : ndarray
        data with background subtracted
    bg : ndarray
        background that was subtracted from the data
    """
    # get dimension of data
    ndim = data.ndim
    # set spacing to 1 if not specified
    if spacing is None:
        spacing = np.ones(ndim)
    else:
        spacing = _normalize_sequence(spacing, ndim)
    # arrayify radius
    radius = np.asarray(_normalize_sequence(ball_radius, ndim))
    # generate the mesh for the sphere
    mesh = np.array(
        np.meshgrid(
            *[np.arange(-r, r + s, s) for r, s in zip(radius, spacing)],
            indexing="ij"))
    # make the sphere and replace nan with 0
    structure = 2 * np.sqrt(
        np.fmax(0, 1 -
                ((mesh / radius.reshape(-1, *((1, ) * ndim)))**2).sum(0)))
    # roll ball on top or bottom dpending on request
    if not top:
        # ndi.white_tophat(y, structure=structure, output=background)
        background = ndi.grey_erosion(data, structure=structure, **kwargs)
        background = ndi.grey_dilation(background,
                                       structure=structure,
                                       **kwargs)
    else:
        # ndi.black_tophat(y, structure=structure, output=background)
        background = ndi.grey_dilation(data, structure=structure, **kwargs)
        background = ndi.grey_erosion(background,
                                      structure=structure,
                                      **kwargs)

    return data - background, background