Example #1
0
def deformation_distance(deformation_field, sink = None, scale = None):
  """Compute the distance field from a deformation vector field.
  
  Arguments
  ---------
  deformation_field : str or array
    Source of the deformation field determined by :func:`deformation_field`.
  sink : str or None
    Image sink to save the deformation field to.
  scale : tuple or None
    Scale factor for each dimension, if None = (1,1,1).
      
  Returns
  -------
  deformation_distannce : array or st
    Array or file name of the deformation distance data.
  """
  
  deformation_field = io.read(deformation_field);
  
  df = np.square(deformation_field);
  if not scale is None:
      for i in range(3):
          df[:,:,:,i] = df[:,:,:,i] * (scale[i] * scale[i]);
  df = np.sqrt(np.sum(df, axis = 3));
  
  return io.write(sink, df);
Example #2
0
def write_color_annotation(filename, annotation_file=None):
    """Creates a rgb image from the atlas color data.
  
  Arguments
  ---------
  filename : str
    The name of the color palette file.
  annotation_file : str
    File name of the atals annotation. 
  
  Returns
  -------
  filename : str
    The name of the file to which the color atlas was written.
  """
    #load atlas and convert to order
    if annotation_file is None:
        annotation_file = annotation.annotation_file
    atlas = np.array(io.read(annotation_file), dtype=int)
    atlas = convert_label(atlas, key='id', value='order', method='map')

    #apply color map
    cm = color_map(alpha=False, as_int=True)
    atlas = cm[atlas]

    return io.write(filename, atlas)
Example #3
0
def label_points(points,
                 annotation_file=None,
                 invalid=0,
                 key='order',
                 level=None):
    """Label points according to the annotation in the labeled image file.
  
  Arguments
  ---------
  points : array 
    Array of nxdim point coordinates to be labeled.
  annotation_file : str
    File name of the atals annotation.
  invalid : int
    Label for invalid points.
  key : str
    The key of the label, by default the order of the labels.
  
  Returns
  -------
  label : array
    Label of the points corresponding to the given key.
  """
    n_points = points.shape[0]
    n_dim = points.shape[1]

    if annotation_file is None:
        annotation_file = annotation.annotation_file

    atlas = io.read(annotation_file)
    atlas = np.array(atlas, dtype=int)
    atlas_shape = atlas.shape

    label = np.full(n_points, invalid, dtype=int)

    points_int = np.asarray(points, dtype=int)
    for d in range(n_dim):
        if d == 0:
            valid = np.logical_and(points_int[:, d] >= 0,
                                   points_int[:, d] < atlas_shape[d])
        else:
            valid = np.logical_and(
                valid,
                np.logical_and(points_int[:, d] >= 0,
                               points_int[:, d] < atlas_shape[d]))

    indices = [points_int[valid, d] for d in range(n_dim)]
    label[valid] = atlas[indices]

    if key != 'id' or level is not None:
        label[valid] = convert_label(label[valid],
                                     key='id',
                                     value=key,
                                     level=level)

    return label
Example #4
0
def write_points(filename, points, indices = False, binary = True):
  """Write points as elastix/transformix point file
  
  Arguments
  ---------
  filename : str
    File name of the elastix point file.
  points : array or str
    Source of the points.
  indices : bool
    Write as pixel indices or physical coordiantes.
  
  Returns
  -------
  filename: str
    File name of the elastix point file.
  """
  
  points = io.read(points);
  
  if binary:
    with open(filename, 'wb') as pointfile:
      if indices:
        np.array(1, dtype = np.int64).tofile(pointfile)
      else:
        np.array(0, dtype = np.int64).tofile(pointfile)
        
      num_points = np.array(len(points), dtype = np.int64);
      num_points.tofile(pointfile);

      points = np.asarray(points, dtype = np.double);
      points.tofile(pointfile);

      pointfile.close();        
      
  else:
    with open(filename, 'w') as pointfile:
      if indices:
        pointfile.write('index\n')
      else:
        pointfile.write('point\n')
    
      pointfile.write(str(points.shape[0]) + '\n');
      np.savetxt(pointfile, points, delimiter = ' ', newline = '\n', fmt = '%.5e')
      pointfile.close();
  
  return filename;
Example #5
0
def average(source,
            sink=None,
            shape=None,
            dtype=None,
            weights=None,
            indices=None,
            kernel=None,
            return_counts=False,
            processes=None,
            verbose=False):
    """Averages a list of points into an volumetric image array.
  
  Arguments
  ---------
  source : str, array or Source
    Source of point of nxd coordinates.
  sink : str, array or None
    The sink for the devolved image, if None return array.
  shape : tuple, str or None
    Shape of the final devolved data. If None, determine from points.
    If str, determine shape from the source at the specified location.
  dtype : dtype or None
    Optional data type of the sink.
  weights : array or None
    Weight array of length n for each point. If None, use uniform weights.  
  method : str
    Method for voxelization: 'sphere', 'rectangle' or 'pixel'.
  indices : array 
    The relative indices to the center to devolve over as nxd array.
  kernel : array
    Optional kernel weights for each index in indices.
  processes : int or None
    Number of processes to use.
  verbose : bool
    If True, print progress info.                        
 
  Returns
  -------
  sink : str, array
    Volumetric data of devolved point data.
  """
    processes, timer = ap.initialize_processing(processes=processes,
                                                verbose=verbose,
                                                function='devolve')

    #points, points_buffer = ap.initialize_source(points);
    points_buffer = io.read(source)
    if points_buffer.ndim == 1:
        points_buffer = points_buffer[:, None]

    if sink is None and shape is None:
        if points_buffer.ndim > 1:
            shape = tuple(
                int(math.ceil(points_buffer[:, d].max()))
                for d in range(points_buffer.shape[1]))
        else:
            shape = (int(math.ceil(points_buffer[:].max())), )
    elif isinstance(shape, str):
        shape = io.shape(shape)

    if sink is None and dtype is None:
        if weights is not None:
            dtype = io.dtype(weights)
        elif kernel is not None:
            kernel = np.asarray(kernel)
            dtype = kernel.dtype
        else:
            dtype = int

    sink, sink_buffer, sink_shape, sink_strides = ap.initialize_sink(
        sink=sink,
        shape=shape,
        dtype=dtype,
        return_shape=True,
        return_strides=True,
        as_1d=True)

    #TODO: initialize properly
    counts = np.zeros(sink_shape, dtype=int, order=sink.order)
    counts_buffer = counts.reshape(-1, order='A')
    #print(counts.shape, counts_buffer.shape)

    if indices is None:
        return sink
    indices = np.asarray(indices, dtype=int)
    if indices.ndim == 1:
        indices = indices[:, None]

    if kernel is not None:
        kernel = np.asarray(kernel, dtype=float)

    #print(kernel);
    #print(weights)
    #return;

    code.average(points_buffer, weights, indices, sink_buffer, sink_shape,
                 sink_strides, counts_buffer, processes)
    #  if weights is None:
    #    if kernel is None:
    #      code.devolve_uniform(points_buffer, indices, sink_buffer, sink_shape, sink_strides, processes);
    #    else:
    #      code.devolve_uniform_kernel(points_buffer, indices, kernel, sink_buffer, sink_shape, sink_strides, processes);
    #  else:
    #    if kernel is None:
    #      code.devolve_weights(points_buffer, weights, indices, sink_buffer, sink_shape, sink_strides, processes);
    #    else:
    #      code.devolve_weights_kernel(points_buffer, weights, indices, kernel, sink_buffer, sink_shape, sink_strides, processes);
    #TODO: move to code
    good = counts_buffer > 0
    sink_buffer[good] /= counts_buffer[good]

    ap.finalize_processing(verbose=verbose, function='devolve', timer=timer)

    if return_counts:
        return sink, counts
    else:
        return sink
Example #6
0
def transform_points(source, sink = None, transform_parameter_file = None, transform_directory = None, indices = False, result_directory = None, temp_file = None, binary = True):
  """Transform coordinates math:`x` via elastix estimated transformation to :math:`T(x)`.

  Arguments
  ---------
  source : str
    Source of the points.
  sink : str or None
    Sink for transformed points.
  transform_parameter_file : str or None
    Parameter file for the primary transformation. 
    If None, the file is determined from the transform_directory.
  transform_directory : str or None
    Result directory of elastix alignment.
    If None the transform_parameter_file has to be given.
  indices : bool
    If True use points as pixel coordinates otherwise spatial coordinates.
  result_directory : str or None
    Elastic result directory.
  temp_file : str or None
    Optional file name for the elastix point file.
      
  Returns
  -------
  points : array or st
    Array or file name of transformed points.
      
  Note
  ----
  The transformation is from the fixed image coorindates to the moving 
  image coordiantes.
  """   
  check_elastix_initialized();    

  # input point file
  if temp_file == None:
    if binary:
      temp_file = os.path.join(tempfile.gettempdir(), 'elastix_input.bin');
    else:
      temp_file = os.path.join(tempfile.gettempdir(), 'elastix_input.txt');
  
  delete_point_file = None;
  if isinstance(source, str):
    if len(source) > 3 and source[-3:] in ['txt', 'bin']:
      if source[-3:] == 'txt':
        binary = False; 
      if source[-3] == 'bin':
        binary = True;
      pointfile = source;
    else:
      points = io.read(source);
      pointfile = temp_file;
      delete_point_file = temp_file;
      write_points(pointfile, points, indices = indices, binary = binary);
  elif isinstance(source, np.ndarray):
    pointfile = temp_file;
    delete_point_file = temp_file;
    write_points(pointfile, source, indices = indices, binary = binary);
  else:
    raise RuntimeError('transform_points: source not string or array!');
  #print(pointfile)
  
  # result directory
  if result_directory == None:
    outdirname = os.path.join(tempfile.gettempdir(), 'elastix_output');
    delete_result_directory = outdirname;
  else:
    outdirname = result_directory;
    delete_result_directory = None;
      
  if not os.path.exists(outdirname):
    os.makedirs(outdirname);
  
  #transform
  transform_parameter_dir, transform_parameter_file = transform_directory_and_file(transform_parameter_file = transform_parameter_file, transform_directory = transform_directory);
  set_path_transform_files(transform_parameter_dir);
  
  #run transformix   
  cmd = '%s -def %s -out %s -tp %s' % (transformix_binary, pointfile, outdirname, transform_parameter_file);
  print(cmd)
  
  res = os.system(cmd);
  
  if res != 0:
    raise RuntimeError('failed executing ' + cmd);
  
  # read data and clean up
  if delete_point_file is not None:
    os.remove(delete_point_file);
  
  #read data / file 
  if sink == []: # return sink as file name
    if binary:
      return os.path.join(outdirname, 'outputpoints.bin')
    else:
      return os.path.join(outdirname, 'outputpoints.txt')
  
  else:
    if binary:
      transpoints = read_points(os.path.join(outdirname, 'outputpoints.bin'), indices = indices, binary = True);
    else:
      transpoints = read_points(os.path.join(outdirname, 'outputpoints.txt'), indices = indices, binary = False); 
    
    if delete_result_directory is not None:
      shutil.rmtree(delete_result_directory);
  
  return io.write(sink, transpoints);
Example #7
0
def deformation_field(sink = [], transform_parameter_file = None, transform_directory = None, result_directory = None):
  """Create the deformation field T(x) - x.
      
  Arguments
  ---------
  sink : str, [] or None
    Image sink to save the transformation field; if [] return the default name 
    of the data file generated by transformix.
  transform_parameter_file : str or None
    Parameter file for the primary transformation, if None, the file is 
    determined from the transform_directory.
  transform_directory : str or None
    Result directory of elastix alignment, if None the 
    transform_parameter_file has to be given.
  result_directory : str or None
    The directorty for the transformix results.
      
  Returns
  -------
  deformation_field : array or str
    Array or file name of the deformation field data.
      
  Note
  ----
  The map determined by elastix is 
  :math:`T \\mathrm{fixed} \\rightarrow \\mathrm{moving}`.
  """
  check_elastix_initialized();   
  
  # result directory
  delete_result_directory = None;
  if result_directory == None:
    resultdirname = os.path.join(tempfile.gettempdir(), 'elastix_output');
    delete_result_directory = resultdirname;
  else:
    resultdirname = result_directory;
      
  if not os.path.exists(resultdirname):
    os.makedirs(resultdirname);
     
  # setup transformation 
  transform_parameter_dir, transform_parameter_file = transform_directory_and_file(transform_parameter_file = transform_parameter_file, transform_directory = transform_directory); 
  set_path_transform_files(transform_parameter_dir);
 
  #transformix -in inputImage.ext -out outputDirectory -tp TransformParameters.txt
  cmd = '%s -def all -out %s -tp  %s' % (transformix_binary, resultdirname, transform_parameter_file)
  
  res = os.system(cmd);
  
  if res != 0:
    raise RuntimeError('deformation_field: failed executing: ' + cmd);
  
  # read result and clean up
  if sink == []:
    return result_data_file(resultdirname);
  elif sink is None:
    resultfile = result_data_file(resultdirname);
    result = io.read(resultfile);
  elif isinstance(sink, str):
    resultfile = result_data_file(resultdirname);
    result = io.convert(resultfile, sink);
  else:
    raise RuntimeError('deformation_field: sink not valid!');
      
  if delete_result_directory is not None:
    shutil.rmtree(delete_result_directory);
  
  return result;
Example #8
0
def transform(source, sink = [], transform_parameter_file = None, transform_directory = None, result_directory = None):
  """Transform a raw data set to reference using the elastix alignment results.
  
  Arguments
  ---------
  source : str or array
    Image source to be transformed.
  sink : str, [] or None
    Image sink to save transformed image to. If [] return the default name 
    of the data file generated by transformix.
  transform_parameter_file : str or None
    Parameter file for the primary transformation. 
    If None, the file is determined from the transform_directory.
  transform_directory : str or None
    Result directory of elastix alignment. 
    If None the transform_parameter_file has to be given.
  result_directory : str or None
    The directorty for the transformix results.
      
  Returns
  -------
  transformed : array or st
    Array or file name of the transformed data.
      
  Note
  ----
  If the map determined by elastix is
  :math:`T: \\mathrm{fixed} \\rightarrow \\mathrm{moving}`, 
  transformix on data works as :math:`T^{-1}(\\mathrm{data})`.
  """
  check_elastix_initialized();  
  
  # image
  source = io.as_source(source);
  if isinstance(source, io.tif.Source):
    imgname = source.location;
    delete_image = None;
  else:
    imgname = os.path.join(tempfile.gettempdir(), 'elastix_input.tif');
    io.write(source, imgname);
    delete_image = imgname;

  # result directory
  delete_result_directory = None;
  if result_directory == None:
    resultdirname = os.path.join(tempfile.gettempdir(), 'elastix_output');
    delete_result_directory = resultdirname;
  else:
    resultdirname = result_directory;
     
  if not os.path.exists(resultdirname):
    os.makedirs(resultdirname);
  
  # tranformation parameter
  transform_parameter_dir, transform_parameter_file = transform_directory_and_file(transform_parameter_file = transform_parameter_file, transform_directory = transform_directory);
  
  set_path_transform_files(transform_parameter_dir);
 
  #transformix -in inputImage.ext -out outputDirectory -tp TransformParameters.txx
  cmd = '%s -in %s -out %s -tp %s' % (transformix_binary, imgname, resultdirname, transform_parameter_file);
  
  res = os.system(cmd);
  
  if res != 0:
    raise RuntimeError('transform_data: failed executing: ' + cmd);
  
  # read data and clean up
  if delete_image is not None:
      os.remove(delete_image);
  
  if sink == []:
    return result_data_file(resultdirname);
  elif sink is None:
    resultfile = result_data_file(resultdirname);
    result = io.read(resultfile);
  elif isinstance(sink, str):
    resultfile = result_data_file(resultdirname);
    result = io.convert(resultfile, sink);
  else:
    raise RuntimeError('transform_data: sink not valid!');
    
  if delete_result_directory is not None:
    shutil.rmtree(delete_result_directory);
  
  return result;
Example #9
0
def resample_points_inverse(source, sink = None, resample_source = None, resample_sink = None,
                            orientation = None, source_shape = None, sink_shape = None, 
                            source_resolution = None, sink_resolution = None, **args):
  """Resample points from original coordiantes to resampled ones.
  
  Arguments
  ---------
  source : str or array
    Points to be resampled inversely.
  sink : str or None
    Sink for the inversly resmapled points.
  resample_source : str, array or None
    Optional source as in :func:`resample`.
  resample_sink: str, array or None
    Optional sink used in :func:`resample`.
  orientation : tuple
    Orientation as specified in :func:`resample`.
  source_shape : tuple or None
    Optional value of source_shape as in :func:`resample`.
  source_resolution : tuple or None
    Optional value of source_resolution as in :func:`resample`.
  sink_resolution : tuple or None
    Optional value of sink_resolution as in :func:`resample`.
      
  Returns
  -------
  resmapled : array or str
    Sink for the inversly resampled point coordinates.

  Notes
  -----
  * The resampling of points here corresponds to the inverse resampling of 
    an image in :func:`resample`, i.e. to func:`resample_inverse`
  * The arguments should be passed exactly as in :func:`resample` except source
    and sink that point to the point sources. 
    Use resample_source and resmaple_sink to pass the source and sink values
    used in :func:`resample`.
  """
  #orientation
  orientation = format_orientation(orientation);
  
  #original source info
  if source_shape is None:
    if source_resolution is None and resample_source is None:
      raise ValueError('Either source_shape, source_resolution or resample_source must to be given!')
    if resample_source is not None:
      source_shape = io.shape(resample_source);

  #original sink info
  if sink_shape is None and sink_resolution is None: 
    if resample_sink is None:
      sink_shape = io.shape(source);
    else:
      sink_shape = io.shape(resample_sink);
  
  source_shape, sink_shape, source_resolution, sink_resolution = \
      resample_shape(source_shape=source_shape, sink_shape=sink_shape, 
                     source_resolution=source_resolution, sink_resolution=sink_resolution, 
                     orientation=orientation);
  
  sink_shape_in_source_orientation = orient_shape(sink_shape, orientation, inverse=True);
  
  resample_factor = [float(t)/float(s) for s,t in zip(source_shape, sink_shape_in_source_orientation)];
  
  points = io.read(source);
 
  # reorient points
  if orientation is not None:
    #reverse axes
    reslice = False;
    slicing = [slice(None)] * len(source_shape);
    for d,o in enumerate(orientation):
      if o < 0:
        slicing[d] = slice(None, None, -1);
        reslice = True;
    if reslice:
      points = points[slicing];

    #permute
    per = orientation_to_permuation(orientation);
    points = points.transpose(per);
  
  points = points[:] / resample_factor;
  
  return io.write(sink, points);     
Example #10
0
def resample_inverse(source, sink = None, 
                     resample_source = None, resample_sink = None,
                     orientation = None, 
                     source_shape = None, source_resolution = None, 
                     sink_shape = None, sink_resolution = None, 
                     axes_order = None, method = 'memmap',
                     interpolation = 'linear', 
                     processes = None, verbose = True, **args):
  """Resample data inversely to :func:`resample` routine.
  
  Arguments
  ---------
  source : str, array
    Source to be inversly resampled (e.g. sink in :func:`resample`).
  sink : str or None
    Sink to write the inversly resampled image to.
  resample_source : str, array or None
    Optional source in :func:`resample`.
  resmaple_sink: str, array or None
    Optional sink used in :func:`resample`.
  orientation : tuple
    Orientation as specified as in :func:`resample`.
  source_shape : tuple or None
    Optional value of source_shape as in :func:`resample`.
  source_resolution : tuple or None
    Optional value of source_resolution as in :func:`resample`.
  sink_resolution : tuple or None
    Optional value of sink_resolution as in :func:`resample`.
  processing_directory : str or None
    Optional directory in which to perform resmapling in parallel.
    If None, a temporary directry will be created.
  axis_order : list of tuples of int or None
    The axes pairs along which to resample the data as in :func:`resample`.
  method : 'shared' or 'memmap'
    Method to handle intermediate resampling results. If 'shared' use shared 
    memory, otherwise use a memory map on disk.
  interpolation : str
    Method to use for interpolating to the resmapled image. 
  processes int or None
    Number of processes to use for parallel resampling.
  verbose : bool
    If True, print progress information.
   
  Returns
  -------
  resampled : array or str
     Data or file name of inversly resampled image.

  Notes
  -----
  * All arguments, except source and sink should be passed as :func:`resample`
    to invert the resmapling.
  """   
  source = io.as_source(source);
  ndim = source.ndim;
  dtype = source.dtype;
  
  #orientation
  orientation = format_orientation(orientation);
  orientation_inverse = inverse_orientation(orientation);
  
  #original source info
  if source_shape is None:
    if source_resolution is None and resample_source is None:
      raise ValueError('Either source_shape, source_resolution or resample_source must to be given!')
    if resample_source is not None:
      source_shape = io.shape(resample_source);
  
  #original sink info
  if sink_shape is None and sink_resolution is None: 
    if resample_sink is None:
      sink_shape = io.shape(source);
    else:
      sink_shape = io.shape(resample_sink);
  
  source_shape, sink_shape, source_resolution, sink_resolution = \
      resample_shape(source_shape=source_shape, sink_shape=sink_shape, 
                     source_resolution=source_resolution, sink_resolution=sink_resolution, 
                     orientation=orientation);
  
  sink_shape_in_source_orientation = orient_shape(sink_shape, orientation, inverse=True);
  
  axes_order, shape_order = _axes_order(axes_order, source, source_shape, sink_shape_in_source_orientation);
 
  interpolation = _interpolation_to_cv2(interpolation);                                   

  if processes is None or not processes == 'serial':
      processes = io.mp.cpu_count();
  
  #reversed orientation
  if not orientation is None:
    #reverse axes
    slicing = [slice(None)] * ndim;
    reslice = False;
    for d,o in enumerate(orientation):
      if o < 0:
        slicing[d] = slice(None, None, -1);
        reslice = True;
    if reslice:
      source = source[slicing];   
    
    #re-orient
    per = orientation_to_permuation(orientation_inverse);
    source = io.read(source);
    source = source.transpose(per);
    source = io.sma.as_shared(source);
 
  #reverse resampling steps
  axes_order = axes_order[::-1];
  
  shape_order = shape_order[:-1];
  shape_order = shape_order[::-1];
  shape_order = shape_order + [source_shape]
  #print(axes_order, shape_order)
  
  #reverse resampling
  n_steps = len(axes_order);
  last_source = source;
  delete_files = [];
  #print(last_source)
  for step, axes, shape in zip(range(n_steps), axes_order, shape_order):
    if step == n_steps-1:
      resampled = io.initialize(source=sink, shape=shape, dtype=dtype, memory='shared', as_source=True); 
    else:
      if method == 'shared':
        resampled = io.sma.create(shape, dtype=dtype, order='C', as_source=True);
      else:
        location = tempfile.mktemp() + '.npy';
        resampled = io.mmp.create(location, shape=shape, dtype=dtype, order='C', as_source=True);
        delete_files.append(location);

    #indices for non-resampled axes
    indices = tuple([range(s) for d,s in enumerate(shape) if d not in axes]);
    indices = [i for i in itertools.product(*indices)];
    n_indices = len(indices);
    
    #resample step
    last_source_virtual = last_source.as_virtual();
    resampled_virtual = resampled.as_virtual();
    _resample = ft.partial(_resample_2d, source=last_source_virtual, sink=resampled_virtual, axes=axes, shape=shape, 
                                         interpolation=interpolation, n_indices=n_indices, verbose=verbose)                       
    
    if processes == 'serial': 
      for index in indices:
        _resample(index=index);
    else:
      with concurrent.futures.ProcessPoolExecutor(processes) as executor:
        executor.map(_resample, indices);
        
    last_source = resampled;
  
  for f in delete_files:
      io.delete_file(f);  
  
  sink = resampled.as_real();
      
  return sink;
Example #11
0
def voxelize(source,
             sink=None,
             shape=None,
             dtype=None,
             weights=None,
             method='sphere',
             radius=(1, 1, 1),
             kernel=None,
             processes=None,
             verbose=False):
    """Converts a list of points into an volumetric image array
  
  Arguments
  ---------
  source : str, array or Source
    Source of point of nxd coordinates.
  sink : str, array or None
    The sink for the voxelized image, if None return array.
  shape : tuple or None
    Shape of the final voxelized data. If None, deterimine from points.
  dtype : dtype or None
    Optional data type of the sink.
  weights : array or None
    Weight array of length n for each point. If None, use uniform weights.  
  method : str
    Method for voxelization: 'sphere', 'rectangle' or 'pixel'.
  radius : tuple 
    Radius of the voxel region to integrate over.
  kernel : function
    Optional function of distance to set weights in the voxelization.
  processes : int or None
    Number of processes to use.
  verbose : bool
    If True, print progress info.                        
 
  Returns
  -------
  sink : str, array
    Volumetric data of voxelied point data.
  """
    points = io.read(source)

    points_shape = points.shape
    if len(points_shape) > 1:
        ndim = points_shape[1]
    else:
        ndim = 1

    if not hasattr(radius, '__len__'):
        radius = [radius] * ndim
    if len(radius) != ndim:
        raise ValueError(
            'Radius %r and points with shape %r do not match in dimension!' %
            (radius, points_shape))

    if method == 'sphere':
        indices, kernel = search_indices_sphere(radius, kernel)
    elif method == 'rectangle':
        indices, kernel = search_indices_rectangle(radius, kernel)
    elif method == 'pixel':
        indices = np.array(0, dtype=int)
        if kernel is not None:
            kernel = np.array([kernel(0)])
    else:
        raise ValueError(
            "method not 'sphere', 'rectangle', or 'pixel', but %r!" % method)

    return dpl.devolve(points,
                       sink=sink,
                       shape=shape,
                       dtype=dtype,
                       weights=weights,
                       indices=indices,
                       kernel=kernel,
                       processes=processes,
                       verbose=verbose)