Example #1
0
def calc_fft_grid_and_map(beam):
    """Calculate the size of the volumetric grid in which to embed this
    beam's voxel data when taking an FFT. For a spatial FFT from
    nutmeg.fftmod, the spatial/frequency origin of the input and output
    arrays should be at the center of the volume to avoid the index
    re-ordering incurred by the FFT algorithm.

    Also return a map into the flattened volume.

    Parameters
    ----------
    beam : a Beam type

    Returns
    -------
    grid_shape : tuple
        a (2L, 2M, 2N) grid shape tuple
    flat_map : ndarray
        a list of indices into the flattened grid array, corresponding to
        the beam's voxel locations
    """
    import xipy.volume_utils as vu
    import nipy.core.api as ni_api
    # need to place voxel data in a volumetric grid,
    # with "discrete" symmetry about the center voxel --
    # ie, in any dimension with 2N points, N points
    # will be interpreted as being axis points less than 0,
    # and N-1 will be greater
    cmap = beam.coordmap
    vsize = beam.voxelsize
    limits = vu.world_limits(cmap, beam.voxel_indices.max(axis=0)+1)
    sym_index_lims = []
    for dv, lim in zip(vsize, limits):
        ineg, ipos = abs(int(lim[0]/dv)), abs(int(lim[1]/dv))
        if ipos > ineg:
            mx = ipos+1
        else:
            mx = ineg
        sym_index_lims.append( (-mx, mx-1) )

    new_affine = cmap.affine.copy()
    new_affine[:3,-1] = np.array(sym_index_lims)[:,0] * vsize
    new_cmap = ni_api.AffineTransform.from_params(
        cmap.function_domain.coord_names,
        cmap.function_range.coord_names,
        new_affine
        )
    grid_shape = tuple( [-mn*2 for mn, _ in sym_index_lims] )
    new_idc = new_cmap.inverse()(beam.voxels).astype('i')

    # now find the flat map for these voxels
    ni, nj, nk = grid_shape
    strides = np.array( [nj*nk, nk, 1] )
    # this are the map for a given volume
    flat_map = (new_idc*strides).sum(axis=1)

    return grid_shape, flat_map
Example #2
0
    def __init__(self, image, bbox=None, mask=False,
                 grid_spacing=None, interpolation_order=3):
        """
        Creates a new SampledVolumeSlicer
        
        Parameters
        ----------
        image : a NIPY Image
            The image to slice
        bbox : iterable (optional)
            The {x,y,z} limits of the enrounding volume box. If None, then
            slices planes in the natural box of the image. This argument
            is useful for overlaying an image onto another image's volume box
        mask : bool or ndarray (optional)
            A binary mask, with same shape as image, with unmasked points
            marked as True (opposite of MaskedArray convention)
        grid_spacing : iterable (optional)
            New grid spacing for the sliced planes. If None, then the
            natural voxel spacing is used.
        """
        
        xyz_image = ni_api.Image(
            np.asarray(image),
            image.coordmap.reordered_range(xipy_ras)
            )
##                                  reorder_output(image.coordmap, 'xyz'))
        self.coordmap = xyz_image.coordmap
        self.raw_image = xyz_image
        nvox = np.product(self.raw_image.shape)
        # if the volume array of the map is more than 100mb in memory,
        # better use a memmap
        self._use_mmap = nvox*8 > 100e6
        self.interpolator = ImageInterpolator(xyz_image,
                                              order=interpolation_order,
                                              use_mmap=self._use_mmap)
##         if mask is True:
##             mask = compute_mask(np.asarray(self.raw_image), cc=0, m=.1, M=.999)
        if type(mask) is np.ndarray:
            self.update_mask(mask, positive_mask=True)
        else:
            self._masking = False
            self.raw_mask = None
            
        if grid_spacing is None:
            self.grid_spacing = list(vu.voxel_size(image.affine))
        else:
            self.grid_spacing = grid_spacing
        if bbox is None:
            self._nominal_bbox = vu.world_limits(xyz_image)
        else:
            self._nominal_bbox = bbox

        # defines {x,y,z}shape and {x,y,z}grid
        self._define_grids()
Example #3
0
def plot_tfbeam(beam, stats=None, with3d=False):
    from xipy.vis.ortho_viewer import ortho_viewer
    struct = load_spatial_image(beam.coreg.mrpath)
    sman = TimeFreqSnPMaps(stats_results=stats)
    bbox = vu.world_limits(struct)
    bman = TFBeamManager(bbox, bstats_manager=sman)
    bman.update_beam(beam)


    win = ortho_viewer(image=struct, mayavi_viewer=with3d)
    win.make_tool_from_functional_manager(NmTimeFreqWindow, bman)
    win.show()
##     app.exec_()
##     # XYZ: this is truly ugly
    bman.signal_image_props()
    bman.signal_new_image()
    return win
Example #4
0
    def __init__(self, image, bbox=None, mask=False,
                 grid_spacing=None, spatial_axes=None,
                 **interp_kws):
        """
        Creates a new ResampledVolumeSlicer
        
        Parameters
        ----------
        image : a NIPY Image
          The image to slice
        bbox : iterable (optional)
          The {x,y,z} limits of the enrounding volume box. If None, then
          slices planes in the natural box of the image. This argument
          is useful for overlaying an image onto another image's volume box
        mask : bool or ndarray (optional)
          A binary mask, with same shape as image, with unmasked points
          marked as True (opposite of MaskedArray convention)
        grid_spacing : sequence (optional)
          New grid spacing for the sliced planes. If None, then the
          natural voxel spacing is used.
        spatial_axes : sequence (optional)
          Normally the image will not be resampled as long as there is a
          one-to-one correspondence from array axes to spatial axes. However,
          a desired correspondence can be specified here. List in 'x, y, z'
          order.
        interp_kws : dict
          Keyword args for the interpolating machinery.. eg:
          * order -- spline order
          * mode --  Points outside the boundaries of the input are filled
                     according to the given mode ('constant', 'nearest',
                     'reflect' or 'wrap'). Default is 'constant'.
          * cval -- fill value if mode is 'constant'
            
        """

        # XYZ: NEED TO BREAK API HERE FOR MASKED ARRAY
        xyz_image = ni_api.Image(
            image._data,
            image.coordmap.reordered_range(xipy_ras)
            )

        native_spacing = vu.voxel_size(xyz_image.affine)
        zoom_grid = grid_spacing is not None and \
                    (np.array(grid_spacing) != native_spacing).any()
        
        # if no special instructions and image is somewhat aligned,
        # don't bother with rotations
        aligned = vu.is_spatially_aligned(image.coordmap) and not zoom_grid
        self.__resamp_kws = dict()
        if aligned:
            # if aligned, double check that it is also aligned with
            # spatial_axes (if present)
            axes = vu.find_spatial_correspondence(image.coordmap)
            if spatial_axes and axes != spatial_axes:
                # XYZ: IF ARRAY IS ALIGNED IN SOME ORIENTATION, COULD
                # RE-ALIGN WITH "spatial_axes" WITH A SIMPLE TRANSFORM
                aligned = False
                interp_kws['order'] = 0
            else:
                world_image = xyz_image
        if not aligned:
            print 'resampling entire Image volume'
            self.__resamp_kws.update(interp_kws)
            self.__resamp_kws.update(
                dict(grid_spacing=grid_spacing, axis_permutation=spatial_axes)
                )
            world_image = vu.resample_to_world_grid(
                image, **self.__resamp_kws
                )

        

        self.coordmap = world_image.coordmap
        self.image_arr = np.asanyarray(world_image)
        self.grid_spacing = vu.voxel_size(world_image.affine)

        # take down the final bounding box; this will define the
        # field of the overlay plot
        self.bbox = vu.world_limits(world_image)
        

        # now find the logical axis to array axis mapping
        self._ax_lookup = vu.spatial_axes_lookup(self.coordmap)
    
        w_shape = world_image.shape
        # these planes are shaped as if the image_arr were
        # sliced along a given axis
        self.null_planes = [np.ma.masked_all((w_shape[0], w_shape[1]),'B'),
                            np.ma.masked_all((w_shape[0], w_shape[2]),'B'),
                            np.ma.masked_all((w_shape[1], w_shape[2]),'B')]

        # finally, update mask if necessary
        mask = np.ma.getmask(image._data)
        if mask is not np.ma.nomask:
            mask = ni_api.Image(mask, image.coordmap)
            self.update_mask(mask, positive_mask=False)