Switch to side-by-side view

--- a
+++ b/ants/math/get_neighborhood.py
@@ -0,0 +1,187 @@
+
+__all__ = ['get_neighborhood_in_mask',
+           'get_neighborhood_at_voxel']
+
+import numpy as np
+
+import ants
+
+from ants.internal import get_lib_fn
+from ants.decorators import image_method
+
+@image_method
+def get_neighborhood_in_mask(image, mask, radius, physical_coordinates=False,
+                            boundary_condition=None, spatial_info=False, get_gradient=False):
+    """
+    Get neighborhoods for voxels within mask.
+    
+    This converts a scalar image to a matrix with rows that contain neighbors 
+    around a center voxel
+    
+    ANTsR function: `getNeighborhoodInMask`
+
+    Arguments
+    ---------
+    image : ANTsImage
+        image to get values from
+    
+    mask : ANTsImage
+        image indicating which voxels to examine. Each voxel > 0 will be used as the 
+        center of a neighborhood
+    
+    radius : tuple/list
+        array of values for neighborhood radius (in voxels)
+    
+    physical_coordinates : boolean
+        whether voxel indices and offsets should be in voxel or physical coordinates
+    
+    boundary_condition : string (optional)
+        how to handle voxels in a neighborhood, but not in the mask.
+            None : fill values with `NaN`
+            `image` : use image value, even if not in mask
+            `mean` : use mean of all non-NaN values for that neighborhood
+    
+    spatial_info : boolean
+        whether voxel locations and neighborhood offsets should be returned along with pixel values.
+    
+    get_gradient : boolean
+        whether a matrix of gradients (at the center voxel) should be returned in 
+        addition to the value matrix (WIP)
+
+    Returns
+    -------
+    if spatial_info is False:
+        if get_gradient is False:
+            ndarray
+                an array of pixel values where the number of rows is the size of the 
+                neighborhood and there is a column for each voxel
+
+        else if get_gradient is True:
+            dictionary w/ following key-value pairs:
+                values : ndarray
+                    array of pixel values where the number of rows is the size of the 
+                    neighborhood and there is a column for each voxel.
+
+                gradients : ndarray
+                    array providing the gradients at the center voxel of each 
+                    neighborhood
+        
+    else if spatial_info is True:
+        dictionary w/ following key-value pairs:
+            values : ndarray
+                array of pixel values where the number of rows is the size of the 
+                neighborhood and there is a column for each voxel.
+
+            indices : ndarray
+                array provinding the center coordinates for each neighborhood
+
+            offsets : ndarray
+                array providing the offsets from center for each voxel in a neighborhood
+
+    Example
+    -------
+    >>> import ants
+    >>> r16 = ants.image_read(ants.get_ants_data('r16'))
+    >>> mask = ants.get_mask(r16)
+    >>> mat = ants.get_neighborhood_in_mask(r16, mask, radius=(2,2))
+    """
+    if not ants.is_image(image):
+        raise ValueError('image must be ANTsImage type')
+    if not ants.is_image(mask):
+        raise ValueError('mask must be ANTsImage type')
+    if isinstance(radius, (int, float)):
+        radius = [radius]*image.dimension
+    if (not isinstance(radius, (tuple,list))) or (len(radius) != image.dimension):
+        raise ValueError('radius must be tuple or list with length == image.dimension')
+
+    boundary = 0
+    if boundary_condition == 'image':
+        boundary = 1
+    elif boundary_condition == 'mean':
+        boundary = 2
+    
+    libfn = get_lib_fn('getNeighborhoodMatrix%s' % image._libsuffix)
+    retvals = libfn(image.pointer, 
+                    mask.pointer, 
+                    list(radius),
+                    int(physical_coordinates), 
+                    int(boundary),
+                    int(spatial_info),
+                    int(get_gradient))
+
+    if not spatial_info:
+        if get_gradient:
+            retvals['values'] = np.asarray(retvals['values'])
+            retvals['gradients'] = np.asarray(retvals['gradients'])
+        else:
+            retvals = np.asarray(retvals['matrix'])
+    else:
+        retvals['values'] = np.asarray(retvals['values'])
+        retvals['indices'] = np.asarray(retvals['indices'])
+        retvals['offsets'] = np.asarray(retvals['offsets'])
+
+    return retvals
+
+@image_method
+def get_neighborhood_at_voxel(image, center, kernel, physical_coordinates=False):
+    """
+    Get a hypercube neighborhood at a voxel. Get the values in a local 
+    neighborhood of an image.
+    
+    ANTsR function: `getNeighborhoodAtVoxel`
+
+    Arguments
+    ---------
+    image : ANTsImage
+        image to get values from.
+    
+    center : tuple/list
+        indices for neighborhood center
+    
+    kernel : tuple/list
+        either a collection of values for neighborhood radius (in voxels) or 
+        a binary collection of the same dimension as the image, specifying the shape of the neighborhood to extract
+    
+    physical_coordinates : boolean
+        whether voxel indices and offsets should be in voxel 
+        or physical coordinates
+
+    Returns
+    -------
+    dictionary w/ following key-value pairs:
+        values : ndarray
+            array of neighborhood values at the voxel
+
+        indices : ndarray
+            matrix providing the coordinates for each value
+
+    Example
+    -------
+    >>> import ants
+    >>> img = ants.image_read(ants.get_ants_data('r16'))
+    >>> center = (2,2)
+    >>> radius = (3,3)
+    >>> retval = ants.get_neighborhood_at_voxel(img, center, radius)
+    """
+    if not ants.is_image(image):
+        raise ValueError('image must be ANTsImage type')
+
+    if (not isinstance(center, (tuple,list))) or (len(center) != image.dimension):
+        raise ValueError('center must be tuple or list with length == image.dimension')
+
+    if (not isinstance(kernel, (tuple,list))) or (len(kernel) != image.dimension):
+        raise ValueError('kernel must be tuple or list with length == image.dimension')
+
+    radius = [int((k-1)/2) for k in kernel]
+
+    libfn = get_lib_fn('getNeighborhood%s' % image._libsuffix)
+    retvals = libfn(image.pointer, 
+                    list(center), 
+                    list(kernel), 
+                    list(radius), 
+                    int(physical_coordinates))
+    for k in retvals.keys():
+        retvals[k] = np.asarray(retvals[k])
+    return retvals
+
+