Ejemplo n.º 1
0
class GaussianSmooth(Operation):
    """
    Apply a gaussian kernel to the image. Specify FWHM and kernel rank.
    """

    params = (
        Parameter(name='fwhm',
                  type='float',
                  default=2.0,
                  description="""
    Full width at half max of gaussian dist in mm"""),
        Parameter(name='rank_factor',
                  type='int',
                  default=3,
                  description="""
    Make the rank of the smoothing kernel appx this many times the stdev{x,y}"""
                  ),
        Parameter(name='SOS',
                  type='bool',
                  default=True,
                  description="""
    Act only on the sum-of-squares combined image if possible."""),
    )

    @ChannelIndependentOperation
    def run(self, image):
        fwhm_x_pix = self.fwhm / image.isize
        fwhm_y_pix = self.fwhm / image.jsize
        fwhm_scale = (8 * np.log(2))**0.5
        sx = fwhm_x_pix / fwhm_scale
        sy = fwhm_y_pix / fwhm_scale
        scale = self.rank_factor
        M = 2 * int(scale * sy + 0.99)
        N = 2 * int(scale * sx + 0.99)
        image[:] = gaussian_smooth(image[:], sy, sx, (M, N))
Ejemplo n.º 2
0
class UBPC_siemens_1shot(Operation):
    params = (
        Parameter(name='coefs', type='tuple', default=(0, 0, 0, 0, 0)),
        Parameter(name='l', type='float', default=1.0),
    )

    @ChannelAwareOperation
    def run(self, image):
        if image.N1 == image.n_pe and image.fov_x > image.fov_y:
            image.fov_y *= 2
            image.jsize = image.isize
        elif image.fov_y == image.fov_x and image.n_pe > image.N1:
            image.fov_y *= (image.n_pe / image.N1)
            image.jsize = image.isize
        print image.fov_y
        grad = Gradient(image.T_ramp, image.T_flat, image.T0, image.n_pe,
                        image.N1, image.fov_x)
        k = kernel(image, grad, self.coefs)
        for n2 in range(image.n_pe):
            sig = image.cdata[:, :, :, n2, :]
            sigshape = sig.shape
            srows = np.product(sigshape[:-1])
            sig.shape = (srows, image.N1)

            scorr = regularized_solve(k[n2], sig.transpose(),
                                      self.l).transpose()
            ##             scorr = half_solve(k[n2], sig.transpose()).transpose()
            scorr.shape = sigshape
            image.cdata[:, :, :, n2, :] = scorr
        image.use_membuffer(0)
Ejemplo n.º 3
0
class FlipSlices(Operation):
    """
    Flip image slices up-down and left-right (in voxel space, not real space)
    """

    params = (Parameter(name="flipud",
                        type="bool",
                        default=False,
                        description="flip each slice up-down"),
              Parameter(name="fliplr",
                        type="bool",
                        default=False,
                        description="flip each slice left-right"))

    #-------------------------------------------------------------------------
    @ChannelIndependentOperation
    def run(self, image):

        if not self.flipud and not self.fliplr: return
        new_xform = image.orientation_xform.tomatrix()
        if self.fliplr:
            new_xform[:, 0] = -new_xform[:, 0]
        if self.flipud:
            new_xform[:, 1] = -new_xform[:, 1]
        image.transform(new_mapping=new_xform)
Ejemplo n.º 4
0
class PlanarPhaseCorrection(Operation):
    """This is a fairly hacky operation to correct timing offsets in a
    possibly accelerated EPI acquisition, and ACS data when present.
    """
    params = (
        Parameter(name="fov_lim", type="tuple", default=None),
        Parameter(name="mask_noise", type="bool", default=True),
    )

    @ChannelIndependentOperation
    def run(self, image):
        arrays = (image.data, )
        refs = (image.ref_data, )
        samps = (image.n2_sampling, slice(None))
        if hasattr(image, 'acs_data'):
            arrays += (image.acs_data, )
            refs += (image.acs_ref_data, )
        a, b = grappa_sampling(image.shape[-2], int(image.accel), image.n_acs)
        if len(b):
            init_acs_dir = (-1.0)**(a.tolist().index(b[0]))
        else:
            init_acs_dir = 1
        polarities = (1.0, init_acs_dir)
        nr = image.n_ramp
        nf = image.n_flat
        if image.pslabel == 'ep2d_bold_acs_test' or image.accel > 2:
            acs_xleave = int(image.accel)
        else:
            acs_xleave = 1

        xleaves = (1, int(acs_xleave))
        for arr, n2, ref_arr, r, x in zip(arrays, samps, refs, polarities,
                                          xleaves):
            if arr is None:
                continue
            util.ifft1(arr, inplace=True, shift=True)
            # enforce 4D arrays
            arr.shape = (1, ) * (4 - len(arr.shape)) + arr.shape
            ref_arr.shape = (1, ) * (4 - len(ref_arr.shape)) + ref_arr.shape
            sub_samp_slicing = [slice(None)] * len(arr.shape)
            sub_samp_slicing[-2] = n2
            Q2, Q1 = arr[sub_samp_slicing].shape[-2:]
            q1_ax = np.linspace(-Q1 / 2., Q1 / 2., Q1, endpoint=False)
            q2_pattern = r * np.power(-1.0, np.arange(Q2) / x)
            for v in xrange(arr.shape[0]):
                sub_samp_slicing[0] = v
                for sl in range(arr.shape[1]):
                    sub_samp_slicing[1] = sl
                    m = simple_unbal_phase_ramp(ref_arr[v, sl].copy(),
                                                nr,
                                                nf,
                                                image.pref_polarity,
                                                fov_lim=self.fov_lim,
                                                mask_noise=self.mask_noise)
                    soln_pln = (m * q1_ax[None, :]) * q2_pattern[:, None]
                    phs = np.exp(-1j * soln_pln)
                    arr[sub_samp_slicing] *= phs
            # correct for flat dimensions
            arr.shape = tuple([d for d in arr.shape if d > 1])
            util.fft1(arr, inplace=True, shift=True)
Ejemplo n.º 5
0
class RotPlane(Operation):
    """
    The orientations in the operation are taken from ANALYZE orient codes
    and are left-handed. However, if the final image is to be NIFTI type,
    the rotation transform is updated (in the right-handed system).
    """

    params = (
        Parameter(name="orient_target",
                  type="str",
                  default=None,
                  description="""
    Final orientation of the image, taken from ANALYZE orient codes.
    Can be: radiological, transverse, coronal, coronal_flipped, sagittal, and
    sagittal_flipped. Also may be recon_epi."""),
        Parameter(name="force",
                  type="bool",
                  default=False,
                  description="""
    If your image is scanned even slightly off axis from X, Y, or Z in scanner
    space, then the transformation will not proceed. You can, however, force
    the rotation, and imply that the new orient target is the true mapping"""),
    )

    #@ChannelIndependentOperation
    @ChannelAwareOperation
    def run(self, image):

        if (self.orient_target not in xforms.keys() + [
                "recon_epi",
        ]):
            self.log("no xform available for %s" % self.orient_target)
            return
        if self.orient_target == "recon_epi":
            # always swap -x to +y, and -y to +x, and reflect in z
            Ts = np.array([
                [0., -1., 0.],
                [-1., 0., 0.],
                [0., 0., -1.],
            ])
            dest_xform = np.dot(image.orientation_xform.tomatrix(),
                                np.linalg.inv(Ts))
        else:
            dest_xform = xforms.get(self.orient_target, None)

        image.transform(new_mapping=dest_xform, force=self.force)
Ejemplo n.º 6
0
class FixTimeSkew(Operation):
    """
    Use sinc interpolation to shift slice data back-in-time to a point
    corresponding to the beginning of acquisition.
    """

    params = (Parameter(name="data_space",
                        type="str",
                        default="kspace",
                        description="""
    name of space to run op: kspace or imspace."""), )

    @ChannelIndependentOperation
    def run(self, image):
        ##         if not verify_scanner_image(self, image):
        ##             return
        if image.tdim < 2:
            self.log("Cannot interpolate with only one volume")
            return

        nslice = image.kdim
        # slice acquisition can be in some nonlinear order
        # eg: Varian data is acquired in an order like this:
        # [19,17,15,13,11, 9, 7, 5, 3, 1,18,16,14,12,10, 8, 6, 4, 2, 0,]
        # So the phase shift factor c for spatially ordered slices should go:
        # [19/20., 9/20., 18/20., 8/20., ...]

        # --- slices in order of acquistion ---
        acq_order = image.acq_order
        # --- shift factors indexed slice number ---
        shifts = np.array(
            [np.nonzero(acq_order == s)[0] for s in range(nslice)])

        if self.data_space == "imspace":
            image.setData(np.abs(image[:]).astype(np.float32))

        # image-space magnitude interpolation can't be
        # multisegment sensitive, so do it as if it's 1-seg
        if image.nseg == 1 or self.data_space == "imspace":
            for s in range(nslice):
                c = float(shifts[s]) / float(nslice)
                #sl = (slice(0,nvol), slice(s,s+1), slice(0,npe), slice(0,nfe))
                subsampInterp(image[:, s, :, :], c, axis=0)
        else:
            # get the appropriate slicing for sampling type
            #sl1 = self.segn(image,0)
            #sl2 = self.segn(image,1)
            sl1 = image.seg_slicing(0)
            sl2 = image.seg_slicing(1)
            for s in range(nslice):
                # want to shift seg1 forward temporally and seg2 backwards--
                # have them meet halfway (??)
                c1 = -(nslice - shifts[s] - 0.5) / float(nslice)
                c2 = (shifts[s] + 0.5) / float(nslice)
                # interpolate for each segment
                subsampInterp_regular(image[:, s, sl1, :], c1, axis=0)
                subsampInterp_regular(image[:, s, sl2, :], c2, axis=0)
Ejemplo n.º 7
0
class Template(Operation):
    """
    A template for operations, with some pointers on Python math
    (does nothing to the data)
    @param fparm: a floating-point number
    @param iparm: an integer
    """

    #the Parameter objects in params are all constructed with four
    #elements: Parameter(name, type, default, description).
    #If you're not using any Parameters, skip this step

    #This params list is actually a Python "tuple". Examples:
    # 3-tuple: (a, b, c); 2-tuple: (a, b); 1-tuple: (a,)
    #The notation is not 100% obvious: be careful to add the
    #trailing comma when defining only 1 Parameter!

    params = (Parameter(name="fparm",
                        type="float",
                        default=0.75,
                        description="A fractional number"),
              Parameter(name="iparm",
                        type="int",
                        default=4,
                        description="A whole number"))

    #note the definition of run: the declaration MUST be this way
    @ChannelIndependentOperation
    def run(self, image):
        # do something to the ReconImage "image" here...

        #Numeric arrays are indexed in C-order. For a time-series
        #of volumes, image[t,z,y,x] is the voxel at point (x,y,z,t)
        #Be aware that this is the opposite of MATLAB indexing.

        #So, the length of the y-dim and x-dim are always the last 2 dimensions
        (ySize, xSize) = image.shape[-2:]
        # a Python way of iterating (secretly using ReconImage's __iter__)
        for vol in image:
            #every "vol" in this iteration is a 3D DataChunk object;
            #a DataChunk also supports __iter__ and can slice into the data
            for slice in vol:
                #every slice is a 2D DataChunk
                slice[:] = doNothing(slice[:], fparm, iparm, xSize, ySize)
Ejemplo n.º 8
0
class Shift(Operation):
    """
    Allows a user to make arbitrary shifts of the data.
    """
    params = (Parameter(name="yshift",
                        type="int",
                        default=0,
                        description="number of points to shift up and down"),
              Parameter(name="xshift",
                        type="int",
                        default=0,
                        description="number of points to shift left to right"))

    @ChannelIndependentOperation
    def run(self, image):
        if self.xshift:
            shift(image[:], self.xshift, axis=-1)
        if self.yshift:
            shift(image[:], self.yshift, axis=-2)
Ejemplo n.º 9
0
class FermiFilter (Operation):
    """
    Apply a Fermi filter to the image.
    """

    params=(
      Parameter(name="cutoff", type="float", default=0.95,
        description="""
    Distance from the center at which the filter drops to 0.5. Units for
    cutoff are percentage of radius."""),
      Parameter(name="trans_width", type="float", default=0.3,
        description="""
    Transition width for rolloff. Smaller values will result in a sharper
    dropoff."""))

    #-------------------------------------------------------------------------
    @ChannelIndependentOperation
    def run(self, image):
        rows, cols = image.shape[-2:]
        image *= fermi_filter(rows, cols, self.cutoff, self.trans_width)
Ejemplo n.º 10
0
class WriteImage(Operation):
    """
    Write an image to the filesystem.
    """

    params = (Parameter(name="filename",
                        type="str",
                        default="image",
                        description="""
    File name prefix for output (extension is
    determined by the format)."""),
              Parameter(name="suffix",
                        type="str",
                        default=None,
                        description="""
    Over-rides the default suffix behavior."""),
              Parameter(name="filedim",
                        type="int",
                        default=3,
                        description="""
    Number of dimensions per output file."""),
              Parameter(name="format",
                        type="str",
                        default="analyze",
                        description="""
    File format to write image as."""),
              Parameter(name="datatype",
                        type="str",
                        default="magnitude",
                        description="""
    Output datatype options: %s.""" % output_datatypes))

    @ChannelAwareOperation
    def run(self, image):
        image.writeImage(self.filename,
                         format_type=self.format,
                         datatype=self.datatype,
                         targetdim=self.filedim,
                         suffix=self.suffix)
Ejemplo n.º 11
0
class ReadImage(Operation):
    "Read image from file."

    params = (Parameter(name="filename",
                        type="str",
                        default="image",
                        description="""
    File name prefix for output (extension will be determined by
    the format)."""),
              Parameter(name="format",
                        type="str",
                        default=None,
                        description="""
    File format to write image as."""),
              Parameter(name="datatype",
                        type="str",
                        default=None,
                        description="""
    Load incoming data as this data type (default is raw data type;
    only complex32 is supported for FID loading). Available datatypes:
    %s""" % recon_output2dtype.keys()),
              Parameter(name="vrange",
                        type="tuple",
                        default=(),
                        description="""
    Volume range over-ride"""),
              Parameter(name="N1",
                        type="int",
                        default=None,
                        description="""
    Number of freq. encode points to read"""))

    #-------------------------------------------------------------------------
    def run(self):
        return readImage(self.filename,
                         self.format,
                         datatype=self.datatype,
                         vrange=self.vrange)  #, N1=128)
Ejemplo n.º 12
0
class Window(Operation):
    """
    Apodizes the k-space data based on a specified 2D window.
    """
    params = (Parameter(name="win_name",
                        type="str",
                        default="hanning",
                        description="""
    Type of window. Can be blackman, hamming, or hanning."""), )

    @ChannelIndependentOperation
    def run(self, image):
        # multiply the window by each slice of the image
        N.multiply(image[:], getWindow(self.win_name, image.idim, image.jdim),
                   image[:])
Ejemplo n.º 13
0
class ViewImage (Operation):
    """
    Run the sliceview volume viewer.
    """
    params = (
        Parameter(name="title", type="str", default="sliceview",
                  description="optional name for the sliceview window"),
        )
    #-------------------------------------------------------------------------
    @ChannelAwareOperation
    def run(self, image):
        # make this raise any runtime error at actual runtime, instead
        # of load-time
        from recon.visualization.sliceview import sliceview
        dimnames = (image.tdim and ("Time Point",) or ()) + \
                   ("Slice", "Row", "Column",)
        sliceview(image, dimnames, title=self.title)
Ejemplo n.º 14
0
class FillHalfSpace (Operation):
    """
    Implements various methods for filling in a full k-space matrix
    after asymmetric EPI sampling.
    """

    params=(
        Parameter(name="fill_size", type="int", default=0,
            description="""
    The new number of rows to fill to in k-space"""),
        Parameter(name="win_size", type="int", default=8,
            description="""
    Length of transition window between measured k-space and
    filled k-space; a window reduces Gibbs ringing"""),
        Parameter(name="iterations", type="int", default=0,
            description="""
    Number of times to iterate the merge process"""),
        Parameter(name="converge_crit", type="float", default=0,
            description="""
    Stop iteration when the summed absolute difference between
    sucessive reconstructed volumes equals this amount"""),
        Parameter(name="method", type="str", default="zero filled",
            description="""
    Possible values: iterative, hermitian, or zero filled""")
        )

    def phaseMap2D(self, slice):
        (_, nx) = slice.shape
        ny = self.fill_size
        y0 = self.fill_size/2
        fill_slice = N.zeros((ny,nx), N.complex128)
        fill_slice[y0-self.over_fill:y0+self.over_fill,:] = \
                   slice[0:self.over_fill*2,:]
        
        phase_map = ifft2d(fill_slice)
        return phase_map
    
    def imageFromFill2D(self, slice):
        (_, nx) = slice.shape
        ny = self.fill_size
        fill_slice = N.zeros((ny,nx), N.complex128)
        embedIm(slice, fill_slice, self.fill_rows, 0)
        fill_slice[:] = ifft2d(fill_slice)
        return fill_slice

    def HermitianFill(self, Im, n_fill_rows):
        #volumewise
        for sl in Im:
            np, nf = sl.shape
            x1 = np/2+1  # this is where x=1
            # catch x=0 with sub-matrix symmetric with A
            Acomp = sl[np-n_fill_rows+1:,x1-1:].copy()
            Bcomp = sl[np-n_fill_rows+1:,1:x1-1].copy()
            sl[1:n_fill_rows,1:x1] = N.conjugate(N.rot90(Acomp, k=2))
            sl[1:n_fill_rows,x1:] = N.conjugate(N.rot90(Bcomp, k=2))
            mag_fact = abs(sl[n_fill_rows+1]).sum()/abs(sl[n_fill_rows]).sum()
            #print mag_fact
            sl[1:n_fill_rows,:] *= mag_fact

            # fill in row -N/2 and col -M/2 where there's no conj. info
            sl[0] = 0
            sl[0:n_fill_rows,0] = 0

    def mergeFill2D(self, filled, measured, winsize=8):
        wsize_f = float(winsize)
        mergept = self.fill_rows
        fill_win = 0.5*(1 + N.cos(N.pi*(N.arange(wsize_f)/wsize_f)))
        measured_win = 0.5*(1 + N.cos(N.pi + N.pi*(N.arange(wsize_f)/wsize_f)))
        #filled[:mergept,:] = filled[:mergept,:]
        filled[mergept+winsize:,:] = measured[winsize:,:]
        # merge measured data with filled data in winsize merge region
        filled[mergept:mergept+winsize,:] = \
               fill_win[:,N.newaxis]*filled[mergept:mergept+winsize,:] + \
               measured_win[:,N.newaxis]*measured[:winsize,:]

    def cookImage2D(self, volData):
        out = sys.stdout
        (ns, _, nx) = volData.shape
        ny = self.fill_size
        cooked3D = N.zeros((ns,ny,nx), N.complex128)
        for s, slice in enumerate(volData):
            out.write("filling slice %d: "%(s,))
            theta = self.phaseMap2D(slice)
            mag = abs(self.imageFromFill2D(slice))
            cooked = N.zeros((ny, nx), N.complex128)
            prev_power = 0.
            c = self.criterion[1]=="converge" and 100000. or self.iterations
            while c > self.criterion[0]:
                prev_image = cooked.copy()
                cooked = mag*N.exp(1.j*N.angle(theta))
                cooked[:] = fft2d(cooked)
                cooked[self.fill_rows:,:] = slice[:]                
                cooked[:] = ifft2d(cooked)
                diff = abs(cooked-prev_image).sum()
                mag = abs(cooked)

                c = self.criterion[1]=="converge" and diff or c-1
            cooked = mag*N.exp(1.j*N.angle(theta))
            cooked[:] = fft2d(cooked)
            self.mergeFill2D(cooked, slice, winsize=self.win_size)
            cooked3D[s][:] = cooked[:]
            out.write("absolute difference=%f\n"%(diff))
        return cooked3D.astype(volData.dtype)

    @ChannelIndependentOperation
    def run(self, image):
        ny = image.jdim
        self.over_fill = ny - self.fill_size/2
        self.fill_rows = self.fill_size - ny
        
        if self.over_fill < 1:
            self.log("not enough measured data: this method needs a few " \
                     "over-scan lines (sampled past the middle of k-space)")
            return
        if self.fill_size <= ny:
            self.log("fill size is not longer than size of measured data "\
                     "(no filling to be done)")
            return

        if self.method not in ("hermitian", "zero filled"):
            if self.converge_crit > 0 and self.iterations > 0:
                self.log("you cannot specify the convergence criterion OR the"\
                         " number of iterations, NOT both: doing nothing")
                return
            elif self.converge_crit > 0:
                self.criterion = (self.converge_crit,"converge")
            elif self.iterations > 0:
                self.criterion = (0,"iterateN")
            else:
                self.log("no iterative criterion given, default to 5 loops")
                self.criterion = (0,"iterateN")
                self.iterations = 5
                
        old_image = image._subimage(image[:].copy())
        new_shape = list(image.shape)
        new_shape[-2] = self.fill_size
        image.resize(new_shape)
        # often the jsize is set wrong--as if the partial j-dim spans the FOV
        if image.jsize > image.isize:
            image.jsize = image.isize

        for new_vol, old_vol in zip(image, old_image):
            if self.method == "iterative":
                cooked = self.cookImage2D(old_vol[:])                
            elif self.method == "hermitian":
                new_vol[:,-ny:,:] = old_vol[:].copy()
                self.HermitianFill(new_vol[:], self.fill_rows)
                cooked = new_vol[:]
            else:
                cooked = self.kSpaceFill(old_vol[:])
                
            new_vol[:] = cooked[:]
            
    def kSpaceFill(self, vol):
        (ns, _, nx) = vol.shape
        ny = self.fill_size
        fill_vol = N.zeros((ns,ny,nx), N.complex64)
        for s in range(ns):
            embedIm(vol[s], fill_vol[s], self.fill_rows, 0)
        return fill_vol.astype(vol.dtype)
Ejemplo n.º 15
0
class BalPhaseCorrection(Operation):
    """
    Balanced Phase Correction attempts to reduce N/2 ghosting and other
    systematic phase errors by fitting referrence scan data to a system
    model. This can only be run on special balanced reference scan data.
    """
    params = (
        Parameter(name="percentile",
                  type="float",
                  default=90.0,
                  description="""
    Indicates what percentage of "good quality" points to use in the solution.
    """),
        Parameter(name="backplane_adj",
                  type="bool",
                  default=False,
                  description="""
    Try to keep data contaminated by backplane eddy currents out of solution.
    """),
        Parameter(name="fitmeans",
                  type="bool",
                  default=False,
                  description="""
    Fit evn/odd means rather than individual planes.
    """),
    )

    @ChannelIndependentOperation
    def run(self, image):

        if not verify_scanner_image(self, image):
            return -1

        if not hasattr(image, "ref_data") or image.ref_data.shape[0] < 2:
            self.log("Not enough reference volumes, quitting.")
            return -1

        self.volShape = image.shape[-3:]
        inv_ref0 = ifft(image.ref_data[0])
        inv_ref1 = ifft(reverse(image.ref_data[1], axis=-1))

        inv_ref = inv_ref0 * N.conjugate(inv_ref1)

        n_slice, n_pe, n_fe = self.refShape = inv_ref0.shape

        #phs_vol comes back shaped (n_slice, n_pe, lin2-lin1)
        phs_vol = unwrap_ref_volume(inv_ref)

        q1_mask = N.zeros((n_slice, n_pe, n_fe))

        # get slice positions (in order) so we can throw out the ones
        # too close to the backplane of the headcoil (or not ???)
        if self.backplane_adj:
            s_idx = tag_backplane_slices(image)
        else:
            s_idx = range(n_slice)
        q1_mask[s_idx] = 1.0
        q1_mask[s_idx, 0::2, :] = qual_map_mask(phs_vol[s_idx, 0::2, :],
                                                self.percentile)
        q1_mask[s_idx, 1::2, :] = qual_map_mask(phs_vol[s_idx, 1::2, :],
                                                self.percentile)
        theta = N.empty(self.refShape, N.float64)
        s_line = N.arange(n_slice)
        r_line = N.arange(n_fe) - n_fe / 2

        B1, B2, B3 = range(3)

        # planar solution
        nrows = n_slice * n_fe
        M = N.zeros((nrows, 3), N.float64)
        M[:, B1] = N.outer(N.ones(n_slice), r_line).flatten()
        M[:, B2] = N.repeat(s_line, n_fe)
        M[:, B3] = 1.

        A = N.empty((n_slice, 3), N.float64)
        B = N.empty((3, n_fe), N.float64)
        A[:, 0] = 1.
        A[:, 1] = s_line
        A[:, 2] = 1.
        if not self.fitmeans:
            for m in range(n_pe):
                P = N.reshape(0.5 * phs_vol[:, m, :], (nrows, ))
                pt_mask = N.reshape(q1_mask[:, m, :], (nrows, ))
                nz = pt_mask.nonzero()[0]
                Msub = M[nz]
                P = P[nz]
                [u, sv, vt] = N.linalg.svd(Msub, full_matrices=0)
                coefs = N.dot(vt.transpose(),
                              N.dot(N.diag(1 / sv), N.dot(u.transpose(), P)))

                B[0, :] = coefs[B1] * r_line
                B[1, :] = coefs[B2]
                B[2, :] = coefs[B3]
                theta[:, m, :] = N.dot(A, B)
        else:
            for rows in ('evn', 'odd'):
                if rows is 'evn':
                    slicing = (slice(None), slice(0, n_pe, 2), slice(None))
                else:
                    slicing = (slice(None), slice(1, n_pe, 2), slice(None))
                P = N.reshape(0.5 * phs_vol[slicing].mean(axis=-2), (nrows, ))
                pt_mask = q1_mask[slicing].prod(axis=-2)
                pt_mask.shape = (nrows, )
                nz = pt_mask.nonzero()[0]
                Msub = M[nz]
                P = P[nz]
                [u, sv, vt] = N.linalg.svd(Msub, full_matrices=0)
                coefs = N.dot(vt.transpose(),
                              N.dot(N.diag(1 / sv), N.dot(u.transpose(), P)))
                B[0, :] = coefs[B1] * r_line
                B[1, :] = coefs[B2]
                B[2, :] = coefs[B3]
                theta[slicing] = N.dot(A, B)[:, None, :]

        phase = N.exp(-1.j * theta).astype(image[:].dtype)
        from recon.tools import Recon
        if Recon._FAST_ARRAY:
            apply_phase_correction(image[:], phase)
        else:
            for dvol in image:
                apply_phase_correction(dvol[:], phase)
Ejemplo n.º 16
0
class UnbalPhaseCorrection(Operation):
    """
    Unbalanced Phase Correction attempts to reduce N/2 ghosting and other
    systematic phase errors by fitting referrence scan data to a system
    model. This can be run on Varian sequence EPI data acquired in 1 shot,
    multishot linear interleaved, or 2-shot centric sampling.
    """

    params = (
        Parameter(name="percentile",
                  type="float",
                  default=25.0,
                  description="""
    Indicates what percentage of "good quality" points to use in the solution.
    """),
        Parameter(name="shear_correct",
                  type="bool",
                  default=True,
                  description="""
    Attempt to correct shearing caused by Varian gradient DC offset
    """),
        Parameter(name="force_6p_soln",
                  type="bool",
                  default=False,
                  description="""
    Fit the 6 parameter model, even if not attempting shearing correction
    (ie, even if only using 3 parameters in the correction).
    """),
    )

    @ChannelIndependentOperation
    def run(self, image):
        # basic tasks here:
        # 1: data preparation
        # 2: phase unwrapping
        # 3: find mean phase diff lines (2 means or 4, depending on sequence)
        # 4: solve for linear coefficients
        # 5: create correction matrix from coefs
        # 6: apply correction to all image volumes
        #
        # * all linearly-sampled data can be treated in a generalized way by
        #   paying attention to the interleave factor (self.xleave) and
        #   the sampling trajectory+timing of each row (image.epi_trajectory)
        #
        # * centric sampled data, whose k-space trajectory had opposite
        #   directions, needs special treatment: basically the general case
        #   is handled in separated parts

        if not verify_scanner_image(self, image):
            return -1
        if not hasattr(image, "ref_data"):
            self.log("No reference volume, quitting")
            return -1
        if len(image.ref_data.shape) > 3 and image.ref_data.shape[-4] > 1:
            self.log("Could be performing Balanced Phase Correction!")

        self.volShape = image.shape[-3:]
        refVol = image.ref_data[0]
        n_slice, n_ref_rows, n_fe = self.refShape = refVol.shape
        # iscentric says whether kspace is multishot centric;
        # xleave is the factor to which kspace data has been interleaved
        # (in the case of multishot interleave)
        iscentric = image.sampstyle is "centric"
        self.xleave = iscentric and 1 or image.nseg
        self.alpha, self.beta, _, self.ref_alpha = image.epi_trajectory()
        # get slice positions (in order) so we can throw out the ones
        # too close to the backplane of the headcoil
        #self.good_slices = tag_backplane_slices(image)
        self.good_slices = range(n_slice)

        # want to fork the code based on sampling style
        if iscentric:
            theta = self.run_centric(image)
        else:
            theta = self.run_linear(image)

        phase = N.exp(-1.j * theta).astype(image[:].dtype)
        from recon.tools import Recon
        ##         apply_phase_correction(image[:], phase)
        # this is faster??
        if Recon._FAST_ARRAY:
            apply_phase_correction(image[:], phase)
        else:
            for dvol in image:
                apply_phase_correction(dvol[:], phase)

    def run_linear(self, image):
        n_slice, n_ref_rows, n_fe = self.refShape

        N1 = image.shape[-1]
        n_conj_rows = n_ref_rows - self.xleave
        # form the S[u]S*[u+1] array:
        inv_ref = ifft(image.ref_data[0])
        inv_ref = inv_ref[:,:-self.xleave,:] * \
                  N.conjugate(inv_ref[:,self.xleave:,:])

        # Adjust the percentile parameter to reflect the percentage of
        # points that actually have data (not the pctage of all points).
        # Do this by determining the fraction of points that pass an
        # intensity threshold masking step.
        ir_mask = build_3Dmask(N.abs(inv_ref), 0.1)
        self.percentile *= ir_mask.sum() / (n_conj_rows * n_slice * n_fe)

        # partition the phase data based on acquisition order:
        # pos_order, neg_order define which rows in a slice are grouped
        # (remember not to count the lines contaminated by artifact!)
        pos_order = (self.ref_alpha[:n_conj_rows] > 0).nonzero()[0]
        neg_order = (self.ref_alpha[:n_conj_rows] < 0).nonzero()[0]

        # in Varian scans, the phase of the 0th product seems to be
        # contaminated.. so throw it out if there is at least one more
        # even-odd product
        # case < 3 ref rows: can't solve problem
        # case 3 ref rows: p0 from (0,1), n0 from (1,2)
        # case >=4 ref rows: p0 from (2,3), n0 from (1,2) (can kick line 0)
        # if the amount of data can support it, throw out p0
        if len(pos_order) > 1:
            pos_order = pos_order[1:]
        phs_vol = unwrap_ref_volume(inv_ref)
        phs_mean, q1_mask = mean_and_mask(phs_vol[:, pos_order, :],
                                          phs_vol[:, neg_order, :],
                                          self.percentile, self.good_slices)
        ### SOLVE FOR THE SYSTEM PARAMETERS
        if not self.shear_correct:
            if self.force_6p_soln:
                # solve for a1,a2,a3,a4,a5,a6, keep (a1,a3,a5)
                coefs = solve_phase_6d(phs_mean, q1_mask)
                coefs = coefs[0::2]
            else:
                coefs = solve_phase_3d(phs_mean, q1_mask)
            print coefs
            return correction_volume_3d(self.volShape, self.alpha, *coefs)
        else:
            coefs = solve_phase_6d(phs_mean, q1_mask)
            print coefs
            return correction_volume_6d(self.volShape, self.alpha, self.beta,
                                        *coefs)

    def run_centric(self, image):
        # centric sampling for epidw goes [0,..,31] then [-1,..,-32]
        # in index terms this is [32,33,..,63] + [31,30,..,0]

        # solving for angle(S[u]S*[u+1]) is equal to the basic problem for u>=0
        # for u<0:
        # angle(S[u]S*[u+1]) =   2[sign-flip-terms]*(-1)^(u+1) + [shear-terms]
        #                    = -(2[sign-flip-terms]*(-1)^u     - [shear-terms])
        # so by flipping the sign on the phs means data, we can solve for the
        # sign-flipping (raster) terms and the DC offset terms with the same
        # equations.

        n_slice, n_ref_rows, n_fe = self.refShape
        n_vol_rows = self.volShape[-2]
        n_conj_rows = n_ref_rows - 2
        # this is S[u]S*[u+1].. now with n_ref_rows-1 rows
        inv_ref = ifft(image.ref_data[0])
        inv_ref = inv_ref[:, :-1, :] * N.conjugate(inv_ref[:, 1:, :])

        # Adjust the percentile parameter to reflect the percentage of
        # points that actually have data (not the pctage of all points).
        # Do this by determining the fraction of points that pass an
        # intensity threshold masking step.
        ir_mask = build_3Dmask(N.abs(inv_ref), 0.1)
        self.percentile *= ir_mask.sum() / (n_conj_rows * (n_slice * n_fe))

        # in the lower segment, do NOT grab the n_ref_rows/2-th line..
        # its product spans the two segments
        cnj_upper = inv_ref[:, n_ref_rows / 2:, :].copy()
        cnj_lower = inv_ref[:, :n_ref_rows / 2 - 1, :].copy()

        phs_evn_upper = unwrap_ref_volume(cnj_upper[:, 0::2, :])
        phs_odd_upper = unwrap_ref_volume(cnj_upper[:, 1::2, :])
        # 0th phase diff on the upper trajectory is contaminated by eddy curr,
        # throw it out if possible:
        if phs_evn_upper.shape[-2] > 1:
            phs_evn_upper = phs_evn_upper[:, 1:, :]
        phs_evn_lower = unwrap_ref_volume(cnj_lower[:, 0::2, :])
        phs_odd_lower = unwrap_ref_volume(cnj_lower[:, 1::2, :])
        # 0th phase diff on downward trajectory (== S[u]S*[u+1] for u=-30)
        # is contaminated too
        if phs_evn_lower.shape[-2] > 1:
            phs_evn_lower = phs_evn_lower[:, :-1, :]

        phs_mean_upper, q1_mask_upper = \
                        mean_and_mask(phs_evn_upper, phs_odd_upper,
                                      self.percentile, self.good_slices)
        phs_mean_lower, q1_mask_lower = \
                        mean_and_mask(phs_evn_lower, phs_odd_lower,
                                      self.percentile, self.good_slices)
        if not self.shear_correct:
            # for upper (u>=0), solve normal SVD
            if self.force_6p_soln:
                coefs = solve_phase_6d(phs_mean_upper, q1_mask_upper)
                coefs = coefs[0::2]
            else:
                coefs = solve_phase_3d(phs_mean_upper, q1_mask_upper)
            print coefs
            theta_upper = correction_volume_3d(self.volShape, self.alpha,
                                               *coefs)
            # for lower (u < 0), solve with negative data
            if self.force_6p_soln:
                coefs = solve_phase_6d(-phs_mean_lower, q1_mask_lower)
                coefs = coefs[0::2]
            else:
                coefs = solve_phase_3d(-phs_mean_lower, q1_mask_lower)
            print coefs
            theta_lower = correction_volume_3d(self.volShape, self.alpha,
                                               *coefs)
            theta_lower[:,
                        n_vol_rows / 2:, :] = theta_upper[:,
                                                          n_vol_rows / 2:, :]
            return theta_lower
        else:
            # for upper (u>=0), solve normal SVD
            coefs = solve_phase_6d(phs_mean_upper, q1_mask_upper)
            print coefs
            theta_upper = correction_volume_6d(self.volShape, self.alpha,
                                               self.beta, *coefs)
            # for lower (u < 0), solve with negative data
            coefs = solve_phase_6d(-phs_mean_lower, q1_mask_lower)
            print coefs
            theta_lower = correction_volume_6d(self.volShape, self.alpha,
                                               self.beta, *coefs)
            theta_lower[:,
                        n_vol_rows / 2:, :] = theta_upper[:,
                                                          n_vol_rows / 2:, :]
            return theta_lower
Ejemplo n.º 17
0
class PreWhitenChannels(Operation):

    params = (Parameter(
        name='go_slow',
        type='bool',
        default=False,
        description="""break up transform into smaller blocks"""), )

    @staticmethod
    def cov_mat(s, full=False):
        from neuroimaging.timeseries import algorithms as alg
        m, n = s.shape
        cm = np.zeros((m, m), 'D')
        _, csd_list = alg.multi_taper_csd(s, BW=5.0 / n)
        for i in xrange(m):
            for j in xrange(i + 1):
                cm[i, j] = np.trapz(csd_list[i][j], dx=1.0 / n)
        if full:
            cm = cm + np.tril(cm, -1).T.conj()
        return cm

    @staticmethod
    def cov_mat_biased(s, full=False):
        m, n = s.shape
        cm = np.zeros((m, m), 'D')
        for i in xrange(m):
            for j in xrange(i + 1):
                cm[i, j] = (s[i].conjugate() * s[j]).mean()
        if full:
            cm = cm + np.tril(cm, -1).T.conj()
        return cm

    @ChannelAwareOperation
    def run(self, image):
        if not hasattr(image, 'n_chan'):
            return
        from recon.scanners import siemens
        n_chan = image.n_chan
        dat = siemens.MemmapDatFile(image.path, n_chan)
        nz_scan = np.empty((image.n_chan, image.M1), 'F')
        nz_scan[:] = dat[:]['data']
        ##         del dat
        ##         image.nz_scan = nz_scan
        covariance = PreWhitenChannels.cov_mat(nz_scan)
        l, v = np.linalg.eigh(covariance, UPLO='L')
        W = np.dot(v, (l**(-1 / 2.))[:, None] * v.conjugate().T).astype('F')
        arr_names = ['cdata', 'cacs_data']
        arrs = [
            getattr(image, arr_name) for arr_name in arr_names
            if hasattr(image, arr_name)
        ]
        if self.go_slow:
            print 'foo'
            for arr in arrs:
                for s in xrange(image.n_slice):
                    sl = arr[:, :, s, :, :].copy()
                    sl_shape = sl.shape
                    sl.shape = (sl_shape[0], np.prod(sl_shape[1:]))
                    arr[:, :, s, :, :] = np.dot(W, sl).reshape(sl_shape)
            del sl
        else:
            for arr, arr_name in zip(arrs, arr_names):
                arr_shape = arr.shape
                arr.shape = (arr.shape[0], np.prod(arr.shape[1:]))
                setattr(image, arr_name, np.dot(W, arr).reshape(arr_shape))
                del arr

##             cd_shape = image.cdata.shape
##             image.cdata.shape = (n_chan, np.prod(cd_shape[1:]))
##             acs_shape = image.cacs_data.shape
##             image.cacs_data.shape = (n_chan, np.prod(acs_shape[1:]))
##             cd = np.dot(W, image.cdata)
##             cd.shape = cd_shape
##             del image.cdata
##             image.cdata = cd
##             acs = np.dot(W, image.cacs_data)
##             acs.shape = acs_shape
##             del image.cacs_data
##             image.cacs_data = acs
        image.use_membuffer(0)
Ejemplo n.º 18
0
class GrappaSynthesize(Operation):
    params = (
        Parameter(name='nblocks', type='int', default=4),
        Parameter(name='sliding', type='bool', default=False),
        Parameter(name='n1_window', type='int', default=None),
        Parameter(name='floating_window',
                  type='bool',
                  default=False,
                  description="""
    Toggles whether the readout neighborhood window is always centered on the
    current column (floating), or whether the neighborhoods are fixed segments.
    """),
        Parameter(name='ft',
                  type='bool',
                  default=False,
                  description="""
    fourier transform readout direction prior to GRAPPA fitting/synthesis
    """),
        Parameter(name='beloud',
                  type='bool',
                  default=False,
                  description="""
    Allow copious information about the GRAPPA process to be printed on screen
    """),
    )

    @ChannelAwareOperation
    def run(self, image):
        accel = int(image.accel)
        sub_data = image.cdata  # shape (nc, nsl, n2, n1)
        acs = image.cacs_data
        # cheap fix for new shape of potentially multi-acquisition acs
        if len(acs.shape) > 4:
            acs = acs[:, 0, :, :, :]
        # transpose to (nsl, nc, n2, n1)
        acs = acs.transpose(1, 0, 2, 3).copy()
        if self.ft:
            print "transforming readout..."
            util.ifft1(sub_data, inplace=True, shift=True)
            util.ifft1(acs, inplace=True, shift=True)

        n2_sampling = image.pe_sampling[:]
        n2_sampling.sort()

        for s in range(image.n_slice):
            N, e = grappa_coefs(acs[s],
                                accel,
                                self.nblocks,
                                sliding=self.sliding,
                                n1_window=self.n1_window,
                                loud=self.beloud,
                                fixed_window=not self.floating_window)
            # find weightings for each slide reconstruction based on the
            # errors of their fits (slide enumeration in axis=-2)
            w = find_weights(e, axis=-2)
            if self.beloud:
                print e
                print w.sum(axis=-2)
            Ssub = sub_data[:, :, s, :, :]
            Ssub[:] = grappa_synthesize(Ssub,
                                        N,
                                        n2_sampling,
                                        weights=w,
                                        loud=self.beloud,
                                        fixed_window=not self.floating_window)

        if self.ft:
            util.fft1(sub_data, inplace=True, shift=True)
            util.fft1(acs, inplace=True, shift=True)
        # by convention, set view to channel 0
        image.use_membuffer(0)
Ejemplo n.º 19
0
class GeometricUndistortionK(Operation):
    """
    Uses a fieldmap to calculate the geometric distortion kernel for each PE
    line in an image, then applies the inverse operator on k-space data to
    correct for field inhomogeneity distortions.
    """

    params = (
        Parameter(name="fmap_file",
                  type="str",
                  default="fieldmap-0",
                  description="""
    Name of the field map file."""),
        Parameter(name="lmbda",
                  type="float",
                  default=8.0,
                  description="""
    Inverse regularization factor."""),
    )

    @ChannelIndependentOperation
    def run(self, image):
        if not verify_scanner_image(self, image):
            return
        fmap_file = clean_name(self.fmap_file)[0]
        ##         if hasattr(image, 'n_chan'):
        ##             fmap_file += '.c%02d'%image.chan
        try:
            fmapIm = readImage(fmap_file)
        except:
            self.log("fieldmap not found: " + fmap_file)
            return -1
        (nslice, npe, nfe) = image.shape[-3:]
        # make sure that the length of the q1 columns of the fmap
        # are AT LEAST equal to that of the image
        regrid_fac = max(npe, fmapIm.shape[-2])
        # fmap and chi-mask are swapped to be of shape (Q1,Q2)
        fmap = np.swapaxes(
            regrid_bilinear(fmapIm[0], regrid_fac, axis=-2).astype(np.float64),
            -1, -2)
        chi = np.swapaxes(regrid_bilinear(fmapIm[1], regrid_fac, axis=-2), -1,
                          -2)
        Q1, Q2 = fmap.shape[-2:]

        # compute T_n2 vector
        Tl = image.T_pe
        delT = image.delT

        a, b, n2, _ = image.epi_trajectory()

        K = get_kernel(Q2, Tl, b, n2, fmap, chi)

        for s in range(nslice):
            # dchunk is shaped (nvol, npe, nfe)
            # inverse transform along nfe (can't do in-place)
            dchunk = ifft1(image[:, s, :, :])
            # now shape is (nfe, npe, nvol)
            dchunk = np.swapaxes(dchunk, 0, 2)
            for fe in range(nfe):
                # want to solve Kx = y for x
                # K is (npe,npe), and y is (npe,nvol)
                #
                # There seems to be a trade-off here as nvol changes...
                # Doing this in two steps is faster for large nvol; I think
                # it takes advantage of the faster BLAS matrix-product in dot
                # as opposed to LAPACK's linear solver. For smaller values
                # of nvol, the overhead seems to outweigh the benefit.
                iK = regularized_inverse(K[s, fe], self.lmbda)
                dchunk[fe] = np.dot(iK, dchunk[fe])
            dchunk = np.swapaxes(dchunk, 0, 2)
            # fft x back to kx, can do inplace here
            fft1(dchunk, inplace=True)
            image[:, s, :, :] = dchunk
Ejemplo n.º 20
0
class ComputeFieldMap(Operation):
    """
    Perform phase unwrapping and calculate B0 inhomogeneity field map.
    """

    params = (
        Parameter(name="fmap_file",
                  type="str",
                  default="fieldmap",
                  description="""
    Name of the field map file to store"""),
        Parameter(name="threshfactor",
                  type="float",
                  default=0.1,
                  description="""
    Adjust this factor to raise or lower the masking threshold for the
    ASEMS signal."""),
    )

    #-------------------------------------------------------------------------
    @ChannelAwareOperation
    def run(self, image):

        # Make sure it's an asems image
        if not hasattr(image, "asym_times") and not hasattr(image, "te"):
            self.log("No asym_time, can't compute field map.")
            return
        asym_times = image.asym_times

        # Make sure there are at least two volumes
        if image.tdim < 2:
            self.log("Cannot calculate field map from only a single volume."\
                     "  Must have at least two volumes.")
            return

        diffshape = (image.tdim - 1, ) + image.shape[-3:]
        diffshape = image.tdim > 2 and diffshape or diffshape[-3:]

        phase_map = image._subimage(np.zeros(diffshape, np.float32))
        bytemask = image._subimage(np.zeros(diffshape, np.float32))

        # Get phase difference between scan n+1 and scan n
        # Then find the mask and unwrapped phase, and compute
        # the field strength in terms of rad/s
        fwhm = max(image.isize, image.jsize) * 1.5
        for psub, msub in zip(phase_map, bytemask):
            del_te = asym_times[msub.num + 1] - asym_times[msub.num]
            if self.haschans:
                for c in range(image.n_chan):
                    image.load_chan(c)
                    dphs = image[msub.num + 1] * np.conjugate(image[msub.num])
                    msk = build_3Dmask(np.power(np.abs(dphs), 0.5),
                                       self.threshfactor)
                    msub[:] += msk
                    ##                     psub[:] += (unwrap3D(np.angle(dphs)) * msk) #/ del_te
                    ##                     psub[:] += gaussian_smooth(np.angle(dphs), 1.5, 3)
                    ##                     smooth_phs = unwrap3D(gaussian_smooth(np.angle(dphs),
                    ##                                                           sigma_y, sigma_x,
                    ##                                                           gaussian_dims))*msk
                    smooth_phs = ReconImage(np.angle(dphs),
                                            isize=image.isize,
                                            jsize=image.jsize)
                    GaussianSmooth(fwhm=fwhm).run(smooth_phs)
                    psub[:] += smooth_phs[:]
                #psub[:] = np.where(msub[:], psub[:]/msub[:], 0)
                psub[:] /= image.n_chan
                msub[:] = np.where(msub[:], 1, 0)
                #psub[:] = (unwrap3D(psub[:]) * msub[:]) #/ del_te
                image.use_membuffer(0)
            else:
                dphs = image[msub.num + 1] * np.conjugate(image[msub.num])
                msub[:] = build_3Dmask(np.power(np.abs(dphs), 0.5),
                                       self.threshfactor)
                #psub[:] = (unwrap3D(np.angle(dphs)) * msub[:]) #/ del_te
                psub[:] = np.angle(dphs)
                GaussianSmooth(fwhm=fwhm).run(psub)
                psub[:] = unwrap3D(psub[:]) * msub[:]
                psub[:] /= del_te

        # for each diff vol, write a file with vol0 = fmap, vol1 = mask
        fmap_file = clean_name(self.fmap_file)[0]
        if phase_map.ndim > 3:
            for index in range(phase_map.tdim):
                catIm = phase_map.subImage(index).concatenate(
                    bytemask.subImage(index), newdim=True)
                catIm.writeImage(fmap_file + "-%d" % index,
                                 format_type="nifti-single")
        else:
            catIm = phase_map.concatenate(bytemask, newdim=True)
            catIm.writeImage(fmap_file, format_type="nifti-single")
Ejemplo n.º 21
0
class ReadoutResampling(Operation):
    """ This operation performs all resampling in the read-out direction.
    These resampling operations are a combination of ramp-sampling correction
    and positive/negative echo timing mis-matches.

    It does not YET reduce oversampling.

    It runs fairly slowly.
    """

    params = (
        Parameter(name="fov_lim", type="tuple", default=None),
        Parameter(name="mask_noise", type="bool", default=True),
    )

    @ChannelAwareOperation
    def run(self, image):
        Tr = image.T_ramp
        Tf = image.T_flat
        T0 = image.T0
        N1 = image.N1
        Tg = 2 * Tr + Tf
        dt = (Tg - 2 * T0) / (N1 - 1)
        As = Tf + Tr - T0**2 / Tr
        t_axis = np.arange(N1) * dt + T0
        nr = image.n_ramp
        nf = image.n_flat
        pe_rev = image.pe_reflected
        pe_sampling = image.pe_sampling
        ref_rev = image.ref_reflected
        acs_sampling = image.acs_sampling
        acs_rev = image.acs_reflected
        acs_ref_rev = image.acs_ref_reflected

        # tn_regrid and neg_tn_regrid are the destination time points
        # for positive and negative lobe readouts
        tn_regrid = image.t_n1()
        neg_tn_regrid = Tg - tn_regrid
        ##         print t_axis
        ##         print tn_regrid, neg_tn_regrid

        ##         tn_regrid = np.empty((N1,), 'd')
        ##         neg_tn_regrid = np.empty_like(tn_regrid)
        ##         idx = np.arange(N1)
        ##         r_up = idx < (nr+1)
        ##         tn_regrid[r_up] = ( T0**2 + 2*idx[r_up]*Tr*As/(N1-1) )**0.5
        ##         r_fl = (idx >= (nr+1)) & (idx < (nf+1))
        ##         tn_regrid[r_fl] = idx[r_fl]*As/(N1-1) + Tr/2 + T0**2/(2*Tr)
        ##         r_dn = idx >= (nf+1)
        ##         tn_regrid[r_dn] = Tg - ( 2*Tr*(Tf+Tr-T0**2/(2*Tr) - idx[r_dn]*As/(N1-1)) )**.5
        ##         neg_tn_regrid = Tg - tn_regrid

        same_grad_diff = pe_rev[1] - pe_rev[0]
        neg_slicing = slice(pe_rev[0], pe_rev[-1] + 1, same_grad_diff)
        pos_sampling = pe_sampling[:]
        for pe in pe_rev:
            pos_sampling.remove(pe)
        pos_slicing = slice(pos_sampling[0], pos_sampling[-1] + 1,
                            same_grad_diff)

        for c in xrange(image.n_chan):
            for v in xrange(image.n_vol):
                for s in xrange(image.n_slice):
                    r = image.cref_data[c, v, s].copy()
                    # polarity is -1 if the first ref was on a neg gradient
                    polarity = ref_rev[0] == 0 and -1 or +1
                    m = sm_utils.simple_unbal_phase_ramp(
                        r, nr, nf, polarity, self.fov_lim, self.mask_noise)
                    Tau = m * dt * N1 / (2 * np.pi)
                    # these are the time points of the acquired sampled
                    tn = (t_axis - Tau)
                    ##                     print Tau, '('+str(dt)+')'
                    # compute these in transpose
                    op_pos = np.sinc((tn_regrid[None, :] - tn[:, None]) / dt)
                    op_neg = np.sinc(
                        (neg_tn_regrid[None, :] - tn[:, None]) / dt)
                    neg_block = image.cdata[c, v, s, neg_slicing].copy()
                    pos_block = image.cdata[c, v, s, pos_slicing].copy()
                    # these are L x N1, to be transformed by N1 x N1 operators
                    # such that S <-- [(N1 x N1)*(L x N1)^T]^T = (L x N1)*(N1 x N1)^T
                    image.cdata[c, v, s,
                                neg_slicing] = np.dot(neg_block, op_neg)
                    image.cdata[c, v, s,
                                pos_slicing] = np.dot(pos_block, op_pos)

        if not acs_sampling:
            return

        acs_offset = -acs_sampling[0]
        # refs in the EPI section are never interleaved
        n_refs = image.n_refs
        n_acs_seg = image.cacs_ref_data.shape[-2] / n_refs
        g = 0
        # transform the acs indicators to be segment-wise
        l = len(acs_sampling)
        acs_sampling = np.array(acs_sampling).reshape(n_acs_seg, l / n_acs_seg)
        l = len(acs_rev)
        acs_rev = np.array(acs_rev).reshape(n_acs_seg, l / n_acs_seg)
        l = len(acs_ref_rev)
        acs_ref_rev = np.array(acs_ref_rev).reshape(n_acs_seg, l / n_acs_seg)
        if len(image.cacs_data.shape) < 5:
            # pad with a rep dimension
            ashape = list(image.cacs_data.shape)
            rshape = list(image.cacs_ref_data.shape)
            ashape.insert(1, 1)
            rshape.insert(1, 1)
            image.cacs_data.shape = tuple(ashape)
            image.cacs_ref_data.shape = tuple(rshape)

        n_acs_rep = image.cacs_data.shape[1]
        for c in xrange(image.n_chan):
            for r in xrange(n_acs_rep):
                for s in xrange(image.n_slice):
                    for g in xrange(n_acs_seg):

                        acs_rev_g = acs_rev[g]
                        sampling_g = acs_sampling[g].tolist()
                        same_grad_diff = acs_rev_g[1] - acs_rev_g[0]
                        for acs in acs_rev_g:
                            sampling_g.remove(acs)
                        neg_slicing = slice(acs_rev_g[0] + acs_offset,
                                            acs_rev_g[-1] + acs_offset,
                                            same_grad_diff)
                        pos_slicing = slice(sampling_g[0] + acs_offset,
                                            sampling_g[-1] + acs_offset,
                                            same_grad_diff)

                        rd = image.cacs_ref_data[c, r, s, g * n_refs:(g + 1) *
                                                 n_refs].copy()
                        polarity = acs_ref_rev[g, 0] == 0 and -1 or +1
                        m = sm_utils.simple_unbal_phase_ramp(
                            rd, nr, nf, polarity, self.fov_lim,
                            self.mask_noise)
                        Tau = m * dt * N1 / (2 * np.pi)
                        ##                     print Tau, '('+str(dt)+')'
                        # these are the time points of the acquired sampled
                        tn = t_axis - Tau
                        # compute these in transpose
                        op_pos = np.sinc(
                            (tn_regrid[None, :] - tn[:, None]) / dt)
                        op_neg = np.sinc(
                            (neg_tn_regrid[None, :] - tn[:, None]) / dt)

                        neg_block = image.cacs_data[c, r, s,
                                                    neg_slicing].copy()
                        pos_block = image.cacs_data[c, r, s,
                                                    pos_slicing].copy()

                        # these are L x N1, to be multiplied by N1 x N1
                        # resampling operators such that
                        # S <-- [(N1 x N1)*(L x N1)^T]^T = (L x N1)*(N1 x N1)^T
                        image.cacs_data[c, r, s, neg_slicing] = np.dot(
                            neg_block, op_neg)
                        image.cacs_data[c, r, s, pos_slicing] = np.dot(
                            pos_block, op_pos)

        image.use_membuffer(0)