def DROI_table(pseudo_path):
     '''
     DROI_table (distance-based ROI table) is a dataframe summarizing all the data from all the
     hemispheres and all the distance-based wedge ROIs used in the visual performance fields
     project.
     '''
     import neuropythy as ny
     df = ny.load(pseudo_path.local_path('DROI_table.csv'))
     df.set_index(['sid', 'hemisphere'])
     return df
 def generate_DROI_tables(self,
                          nprocs=None,
                          printstatus=False,
                          angles=None,
                          eccens=None,
                          min_variance_explained=0,
                          method=None,
                          tempdir=None):
     '''
     generate_DROI_tables() recalculates the set of distance-based ROIs for each subject
     based on the data in the inferred maps and pRFs.
     '''
     import neuropythy as ny, os, six, pyrsistent as pyr, multiprocessing as mp, numpy as np
     drois = {}
     subject_list = self.subject_list
     nsubs = len(subject_list)
     f = VisualPerformanceFieldsDataset._generate_DROI_tables_call
     if nprocs is None or nprocs is Ellipsis: nprocs = mp.cpu_count()
     if nprocs < 2: nprocs = 1
     if nprocs > 1:
         if tempdir is None: tdir = ny.util.tmpdir()
         else: tdir = tempdir
         for ii in np.arange(0, nsubs, nprocs):
             mx = np.min([len(subject_list), ii + nprocs])
             if printstatus:
                 print("%2d - %3d%%" %
                       ((ii + 1) / nsubs * 100, mx / nsubs * 100))
             pool = mp.Pool(nprocs)
             sids = subject_list[ii:ii + nprocs]
             tbls = pool.map(f, [
                 (sid, angles, eccens, tdir, min_variance_explained, method)
                 for sid in sids
             ])
             pool.close()
             # add these data into the overall roi table
             for (sid, tbl) in zip(sids, tbls):
                 drois[sid] = ny.load(tbl)
     else:
         for (ii, sid) in enumerate(subject_list):
             if printstatus and ii % 10 == 9:
                 print('%3d (%d), %4.1f%%' %
                       (ii, sid, ii / len(subject_list) * 100.0))
             drois[sid] = f((sid, angles, eccens, tdir,
                             min_variance_explained, method))
     return pyr.pmap(drois)
Exemple #3
0
 tt = {}
 # Load the params first:
 try:
     with open(os.path.join(tdir, 'params.json'), 'r') as fl:
         params = json.load(fl)
 except Exception:
     warn('Could not load params.json file in dir: %s' % (tdir, ))
     continue
 # Then get the stimfile:
 stimfile = params.get('stimulus_file', 'stimulus.nii.gz')
 if not os.path.isabs(stimfile): stimfile = os.path.join(tdir, stimfile)
 if not os.path.isfile(stimfile):
     warn('Could not find designated stimfile (%s) in test dir %s!' %
          (stimfile, tdir))
     continue
 stim = ny.load(stimfile, to='image')
 # Then the data:
 datafile = params.get('data_file', 'data.nii.gz')
 if not os.path.isabs(datafile): datafile = os.path.join(tdir, datafile)
 if not os.path.isfile(datafile):
     warn('Could not find designated datafile (%s) in test dir %s!' %
          (datafile, tdir))
     continue
 data = ny.load(datafile, to='image')
 # Okay, make sure there is an indication of the screen width:
 if 'pixels_per_degree' not in params:
     if 'screen_width' in params and 'screen_distance' in params:
         sw = params['screen_width']
         sd = params['screen_distance']
         d2p = 180 / np.pi * 2 * np.arctan2(sw / 2, sd)
     else:
Exemple #4
0
# Additional matplotlib preferences:
font_data = {'family':'sans-serif',
             'sans-serif':['Helvetica Neue', 'Helvetica', 'Arial'],
             'size': 10,
             'weight': 'light'}
mpl.rc('font',**font_data)
# we want relatively high-res images, especially when saving to disk.
mpl.rcParams['figure.dpi'] = 72*2
mpl.rcParams['savefig.dpi'] = 72*4


# %% markdown
# #Load Data
# %
# This is for the original 4x4 nifti
nii1 = ny.load('~/toolboxes/PRFmodel/data/examples/synthDataExample2_TR2.nii.gz', to='image')
data = np.reshape(nii1.dataobj, [nii1.shape[0]*nii1.shape[1], nii1.shape[-1]])

# This is for the 1D nifti
# With the 1D nifti I thing we will save lots of orientation problems. For example,
# Freesurfers MRIread read the data in different order than niftiRead, flipping x and y.
# With a 1D, we do a squeeze and that's it.
# nii1       = ny.load('~/toolboxes/PRFmodel/data/examples/synthDataExample3_1D_TR2.nii.gz', to='image')
# data       = np.squeeze(nii1.dataobj)
input_data = data[4,:]  # Noah said he used the 5th voxel for the example

# Stimulus related (we passed it as a nifti, to avoid sending matlab specific things)
nii2 = ny.load('~/toolboxes/PRFmodel/data/examples/Exp-103_binary-true_size-20x20.nii.gz', to='image')
stim = np.squeeze(nii2.dataobj)
# stim = np.roll(stim, -5, axis=2)
stimulus = popeye.visual_stimulus.VisualStimulus(stim.astype('int16'), 30, 10.57962, 0.5, 2, 'int16')
Exemple #5
0
def main(args):
    # Parse the arguments...
    parser = argparse.ArgumentParser()
    parser.add_argument(
        'reg',
        metavar='registration_file',
        nargs=1,
        help=('The distort2anat_tkreg.dat or similar file: the registration'
              ' file, in FreeSurfer\'s tkreg format, to apply to the EPIs.'))
    parser.add_argument(
        'epis',
        metavar='EPI',
        type=str,
        nargs='+',
        help='The EPI files to be converted to anatomical orientation')
    parser.add_argument(
        '-t',
        '--tag',
        required=False,
        default='-',
        dest='tag',
        nargs=1,
        help=('A tag to append to the output filenames; if given as - or'
              ' omitted, overwrites original files.'))
    parser.add_argument(
        '-s',
        '--surf',
        required=False,
        default=False,
        dest='surface',
        action='store_true',
        help=('If provided, instructs the script to also produce files of the '
              'time-series resampled on the cortical surface.'))
    parser.add_argument(
        '-o',
        '--out',
        required=False,
        default='.',
        dest='outdir',
        help=(
            'The output directory to which the files should be written; by'
            ' default this is the current directory (.); note that if this'
            ' directory also contains the EPI files and there is no tag given,'
            ' then the EPIs will be overwritten.'))
    parser.add_argument(
        '-m',
        '--method',
        required=False,
        default='linear',
        dest='method',
        help=('The method to use for volume-to-surface interpolation; this may'
              ' be nearest or linear; the default is linear.'))
    parser.add_argument(
        '-l',
        '--layer',
        required=False,
        default='midgray',
        dest='layer',
        help=(
            'Specifies the cortical layer to user in interpolation from volume'
            ' to surface. By default, uses midgray. May be set to a value'
            ' between 0 (white) and 1 (pial) to specify an intermediate surface'
            ' or may be simply white, pial, or midgray.'))
    parser.add_argument(
        '-d',
        '--subjects-dir',
        required=False,
        default=None,
        dest='sdir',
        help=('Specifies the subjects directory to use; by default uses the'
              ' environment variable SUBJECTS_DIR.'))
    parser.add_argument('-v',
                        '--verbose',
                        required=False,
                        default=False,
                        action='store_true',
                        dest='verbose',
                        help='Print verbose output')
    if args[0].startswith('python'): args = args[2:]
    else: args = args[1:]
    args = parser.parse_args(args)
    # Check some of the arguments...
    epis = args.epis
    if len(epis) < 1: raise RuntimeError('No EPIs given')
    tag = args.tag[0]
    if tag == '-': tag = ''
    dosurf = args.surface
    outdir = args.outdir
    if not os.path.isdir(outdir):
        raise RuntimeError('Directory %s does not exist' % outdir)
    if args.verbose:

        def note(*args):
            six.print_(*args, flush=True)
            return True
    else:

        def note(*args):
            return False

    try:
        args.layer = float(args.layer)
    except:
        pass
    # Read in the registration file
    args.reg = args.reg[0]
    if not os.path.isfile(args.reg):
        raise RuntimeError('Given registration file not found: %s' % args.reg)
    with open(args.reg, 'r') as f:
        lines = []
        while True:
            s = f.readline()
            if s is None or s == '': break
            lines.append(s)
    # This tells us some info...
    sub = lines[0].strip()
    if args.sdir is not None:
        ny.add_subject_path(args.sdir)
    try:
        sub = ny.freesurfer_subject(sub)
    except:
        raise ValueError(
            'No subject %s; you may need to set your SUBJECTS_DIR' % sub)
    affine = np.asarray([[float(ss) for ss in s.split()] for s in lines[4:8]])
    affinv = np.linalg.inv(affine)
    displm = sub.lh.affine
    # loop over the given EPIs
    for epi in epis:
        note('Processing EPI %s...' % epi)
        # import the epi file..
        img = ny.load(epi, to='image')
        # edit the header...
        note('   - Correcting volume orientation...')
        new_affine = np.dot(displm,
                            np.dot(affinv, ny.freesurfer.tkr_vox2ras(img)))
        newimg = nib.Nifti1Image(img.dataobj, new_affine, img.header)
        (epi_dir, epi_flnm) = os.path.split(epi)
        if epi_flnm[:-4] in ['.mgz', '.mgh', '.nii']:
            pre = epi_flnm[:-4]
            suf = epi_flnm[-4:]
        else:
            pre = epi_flnm[:-7]
            suf = epi_flnm[-7:]
        srf_flnm = pre + tag + '.mgz'
        epi_flnm = pre + tag + suf
        newimg.to_filename(os.path.join(args.outdir, epi_flnm))
        # okay, now project to the surface
        if args.surface:
            note('   - Projecting to surface...')
            (ldat, rdat) = sub.image_to_cortex(newimg,
                                               surface=args.layer,
                                               method=args.method,
                                               dtype=np.float32)
            # we need to fix the dimensions...
            for (d, h) in zip([ldat, rdat], ['lh', 'rh']):
                if d.shape[-1] == 1:
                    # then this should properly be a 3d MGH image, not a 4d one.
                    im = nib.freesurfer.mghformat.MGHImage(
                        np.transpose(reduce(np.expand_dims, [-1], d),
                                     (0, 2, 1)), np.eye(4))
                else:
                    im = nib.freesurfer.mghformat.MGHImage(
                        np.transpose(reduce(np.expand_dims, [-1, -1], d),
                                     (0, 2, 3, 1)), np.eye(4))
                im.to_filename(os.path.join(args.outdir, h + '.' + srf_flnm))
    # That's it!
    return 0
Exemple #6
0
def import_benson14_from_freesurfer(freesurfer_subject,
                                    max_eccentricity,
                                    modality='surface',
                                    import_filter=None):
    '''
    import_benson14_from_freesurfer is a calculation that imports (or creates then imports) the
    Benson et al. (2014) template of retinotopy for the subject, whose neuropythy.freesurfer
    Subject object must be provided in the parameter freesurfer_subject. The optional parameter
    modality (default: 'volume') may be either 'volume' or 'surface', and determines if the loaded
    modality is volumetric or surface-based.

    Required afferent parameters:
      @ freesurfer_subject Must be a valid neuropythy.freesurfer.Subject object.
 
    Optional afferent parameters:
      @ modality May be 'volume' or 'surface' to specify the anatomical modality.
      @ max_eccentricity May specifies the maximum eccentricity value to use.
      @ import_filter If specified, may give a function that accepts four parameters:
        f(polar_angle, eccentricity, label, hemi); if this function fails to return True for the 
        appropriate values of a particular vertex/voxel, then that vertex/voxel is not included in
        the prediction.

    Provided efferent values:
      @ polar_angles    Polar angle values for each vertex/voxel.
      @ eccentricities  Eccentricity values for each vertex/voxel.
      @ labels          An integer label 1, 2, or 3 for V1, V2, or V3, one per vertex/voxel.
      @ hemispheres     1 if left, -1 if right for all vertex/voxel.
      @ cortex_indices  For vertices, the vertex index (in the appropriate hemisphere) for each;
                        for voxels, the (i,j,k) voxel index for each.
      @ cortex_coordinates For voxels, this is the (i,j,k) voxel index (same as cortex_indices); for
                        surfaces, this is ths (x,y,z) position of each vertex in surface-space.

    Notes:
      * polar_angles are always given such that a negative polar angle indicates a RH value and a
        positive polar angle inidicates a LH value
      * cortex_indices is different for surface and volume modalities
      * labels will always be 1, 2, or 3 indicating V1, V2, or V3

    '''
    max_eccentricity = max_eccentricity.to(units.deg) if pimms.is_quantity(max_eccentricity) else \
                       max_eccentricity*units.deg
    subject = freesurfer_subject
    if modality.lower() == 'volume':
        # make sure there are template volume files that match this subject
        ang = os.path.join(subject.path, 'mri', 'benson14_angle.mgz')
        ecc = os.path.join(subject.path, 'mri', 'benson14_eccen.mgz')
        lab = os.path.join(subject.path, 'mri', 'benson14_varea.mgz')
        if not os.path.exists(ang) or not os.path.exists(
                ecc) or not os.path.exists(lab):
            # Apply the template first...
            neurocmd.benson14_retinotopy.main(subject.path)
        if not os.path.exists(ang) or not os.path.exists(
                ecc) or not os.path.exists(lab):
            raise ValueError('No areas template found/created for subject: ' +
                             lab)
        angle_mgz = fs.mghformat.load(ang)
        eccen_mgz = fs.mghformat.load(ecc)
        label_mgz = fs.mghformat.load(lab)
        ribbon_mgzs = (subject.mgh_images['lh.ribbon'],
                       subject.mgh_images['rh.ribbon'])
        # The variables are all mgz volumes, so we need to extract the values:
        labels = np.round(np.abs(label_mgz.dataobj.get_unscaled()))
        angles = angle_mgz.dataobj.get_unscaled()
        eccens = eccen_mgz.dataobj.get_unscaled()
        (lrib, rrib) = [r.dataobj.get_unscaled() for r in ribbon_mgzs]
        # Find the voxel indices first:
        # for now we only look at v1-v3
        labels[labels > 3] = 0
        coords = np.asarray(np.where(labels.astype(bool))).T
        # Grab the hemispheres; filter down if something isn't in the ribbon
        tmp = [(1 if lrib[i, j, k] == 1 else -1, (i, j, k))
               for (i, j, k) in coords
               if lrib[i, j, k] != 0 or rrib[i, j, k] != 0
               if eccens[i, j, k] < max_eccentricity.m]
        hemis = np.asarray([r[0] for r in tmp], dtype=np.int)
        idcs = np.asarray([r[1] for r in tmp], dtype=np.int)
        coords = np.asarray(idcs, dtype=np.float)
        # Pull out the angle/eccen data
        angs0 = np.asarray([angles[i, j, k] for (i, j, k) in idcs])
        angles = angs0 * hemis
        eccens = np.asarray([eccens[i, j, k] for (i, j, k) in idcs],
                            dtype=np.float)
        labels = np.asarray([labels[i, j, k] for (i, j, k) in idcs],
                            dtype=np.int)
    elif modality.lower() == 'surface':
        rx = freesurfer_subject.RH.midgray_surface.coordinates.T
        lx = freesurfer_subject.LH.midgray_surface.coordinates.T
        # make sure there are template volume files that match this subject
        lang = os.path.join(subject.path, 'surf', 'lh.benson14_angle.mgz')
        lecc = os.path.join(subject.path, 'surf', 'lh.benson14_eccen.mgz')
        llab = os.path.join(subject.path, 'surf', 'lh.benson14_varea.mgz')
        rang = os.path.join(subject.path, 'surf', 'rh.benson14_angle.mgz')
        recc = os.path.join(subject.path, 'surf', 'rh.benson14_eccen.mgz')
        rlab = os.path.join(subject.path, 'surf', 'rh.benson14_varea.mgz')
        (lang, lecc, llab, rang, recc, rlab) = [
            flnm if os.path.isfile(flnm) else flnm[:-4]
            for flnm in (lang, lecc, llab, rang, recc, rlab)
        ]
        if not os.path.exists(lang) or not os.path.exists(rang) or \
           not os.path.exists(lecc) or not os.path.exists(recc) or \
           not os.path.exists(llab) or not os.path.exists(rlab):
            # Apply the template first...
            neurocmd.benson14_retinotopy.main(subject.path)
        if not os.path.exists(lang) or not os.path.exists(rang) or \
           not os.path.exists(lecc) or not os.path.exists(recc) or \
           not os.path.exists(llab) or not os.path.exists(rlab):
            raise ValueError(
                'No anatomical template found/created for subject')
        (lang, lecc, llab, rang, recc, rlab) = [
            neuro.load(fl) for fl in (lang, lecc, llab, rang, recc, rlab)
        ]
        llab = np.round(np.abs(llab))
        rlab = np.round(np.abs(rlab))
        (angs0, eccs, labs) = [
            np.concatenate([ldat, rdat], axis=0)
            for (ldat, rdat) in zip([lang, lecc, llab], [rang, recc, rlab])
        ]
        idcs = np.concatenate([range(len(lang)), range(len(rang))], axis=0)
        valid = np.intersect1d(
            np.intersect1d(np.where(labs > 0)[0],
                           np.where(labs < 4)[0]),
            np.where(eccs < max_eccentricity.m)[0])
        idcs = idcs[valid]
        coords = np.concatenate([lx, rx], axis=0)[valid]
        hemis = np.concatenate([[1 for a in lang], [-1 for a in rang]],
                               axis=0)[valid]
        # old versions of the template had positive numbers in both hemispheres...
        if np.mean(angs0[valid[hemis == -1]]) > 0:
            angles = angs0[valid] * hemis
        else:
            angles = angs0[valid]
        eccens = eccs[valid]
        labels = np.asarray(labs[valid], dtype=np.int)
    else:
        raise ValueError('Option modality must be \'surface\' or \'volume\'')
    # do the filtering and convert to pvectors
    if import_filter is None:
        res = {
            'polar_angles': units.degree * angles,
            'eccentricities': units.degree * eccens,
            'labels': labels,
            'cortex_indices': idcs,
            'cortex_coordinates': coords,
            'hemispheres': hemis
        }
    else:
        sels = [
            i for (i, (p, e, l,
                       h)) in enumerate(zip(angles, eccens, labels, hemis))
            if import_filter(p, e, l, h)
        ]
        res = {
            'polar_angles': units.degree * angles[sels],
            'eccentricities': units.degree * eccens[sels],
            'labels': labels[sels],
            'cortex_indices': idcs[sels],
            'cortex_coordinates': coords[sels],
            'hemispheres': hemis[sels]
        }
    # make sure they're all write-only
    for v in res.itervalues():
        v.setflags(write=False)
    return res
 def _load_DROI(sid):
     # get a subject-specific cache_path
     cpath = pseudo_path.local_path('DROIs', '%s.csv' % (sid, ))
     return ny.load(cpath)
 def _load_distances(sid, h):
     flnm = pseudo_path.local_path('distances', '%s_%s.mgz' % (sid, h))
     (v, d, h) = load(flnm).T
     return pimms.persist({'ventral': v, 'dorsal': d, 'horizontal': h})
 def _load_infmaps(sid, h, patt):
     flnm = pseudo_path.local_path('inferred_maps', patt % (sid, h))
     return load(flnm)
Exemple #10
0
def main(args):
    # Parse the arguments...
    parser = argparse.ArgumentParser()
    parser.add_argument('sub', metavar='subject', nargs=1,
                        help=('The FreeSurfer subject ID or directory.'))
    parser.add_argument('outdir', metavar='directory', type=str, nargs=1,
                        help='The directory containing the output nifti files.')
    parser.add_argument('-x', '--no-surf', required=False, default=True,
                        dest='surface', action='store_false',
                        help=('If provided, instructs the script not to produce files of the '
                              'pRF parameters resampled onto the cortical surface.'))
    parser.add_argument('-m', '--method', required=False, default='linear', dest='method',
                        help=('The method to use for volume-to-surface interpolation; this may'
                              ' be nearest or linear; the default is linear.'))
    parser.add_argument('-l', '--layer', required=False, default='midgray', dest='layer',
                        help=('Specifies the cortical layer to user in interpolation from volume'
                              ' to surface. By default, uses midgray. May be set to a value'
                              ' between 0 (white) and 1 (pial) to specify an intermediate surface'
                              ' or may be simply white, pial, or midgray.'))
    parser.add_argument('-d', '--subjects-dir', required=False, default=None, dest='sdir',
                        help=('Specifies the subjects directory to use; by default uses the'
                              ' environment variable SUBJECTS_DIR.'))
    parser.add_argument('-y', '--no-invert-y', required=False, default=True, dest='invert_y',
                        action='store_false',
                        help=('If provided, does not invert the y-coordinate exported from'
                              ' VistaSoft; by default this inversion is performed.'))
    parser.add_argument('-v', '--verbose', required=False, default=False, action='store_true',
                        dest='verbose', help='Print verbose output')
    if args[0].startswith('python'): args = args[2:]
    else: args = args[1:]
    args = parser.parse_args(args)
    # Check some of the arguments...
    sub = ny.freesurfer_subject(args.sub[0])
    dosurf = args.surface
    outdir = args.outdir[0]
    if not os.path.isdir(outdir):
        raise RuntimeError('Directory %s does not exist' % outdir)
    else:
        os.chdir(outdir)
    if args.verbose:
        def note(*args):
            six.print_(*args, flush=True)
            return True
    else:
        def note(*args):
            return False
    try: args.layer = float(args.layer)
    except: pass
    # figure out what datasets are here...
    dsets = [fl[:-13] for fl in os.listdir('.') if fl.endswith('-xcrds.nii.gz')]
    # loop over the given EPIs
    for ds in dsets:
        note('Processing Dataset %s...' % ds)
        # import the files...
        note('  - Importing parameters...')
        (x,y,s,v) = [ny.load('%s-%s.nii.gz' % (ds, suff), to='image')
                     for suff in ['xcrds','ycrds','sigma','vexpl']]
        # fix polar angle/eccen
        note('  - Creating polar angle/eccentricity images...')
        ang = np.arctan2(-y.get_data() if args.invert_y else y.get_data(), x.get_data())
        ang = np.mod((90.0 - 180.0/np.pi * ang) + 180, 360) - 180
        a = nib.Nifti1Image(ang, x.affine, x.header)
        e = nib.Nifti1Image(np.sqrt(y.get_data()**2 + x.get_data()**2), x.affine, x.header)
        a.to_filename('%s-angle.nii.gz' % ds)
        e.to_filename('%s-eccen.nii.gz' % ds)
        # surfaces...
        if not dosurf: continue
        note('  - Projecting to surface...')
        to_output = {'xcrds':x, 'ycrds':y, 'sigma': s, 'vexpl': v}
        xy = ([0,0],[0,0])
        for (dname,im) in six.iteritems(to_output):
            (ldat, rdat) = sub.image_to_cortex(im, args.layer,
                                               method=args.method, dtype=np.float32,
                                               weights=v)
            if dname == 'xcrds':
                xy[0][0] = ldat
                xy[1][0] = rdat
            elif dname == 'ycrds':
                xy[0][1] = ldat
                xy[1][1] = rdat
            # we need to fix the dimensions...
            for (d,h) in zip([ldat,rdat], ['lh','rh']):
                ny.save('%s.%s-%s.mgz' % (h, ds, dname), d)
        # Special handling for the angle and eccen (avoids need for circular averaging)
        xy = (np.asarray(xy[0]), np.asarray(xy[1]))
        (lecc,recc) = [np.sqrt(np.sum(u**2, axis=0)) for u in xy]
        (lang,rang) = [np.arctan2(-u[1] if args.invert_y else u[1], u[0])
                       for u in (xy[0] * ny.util.zinv(lecc), xy[1] * ny.util.zinv(recc))]
        (lang,rang) = [np.mod((90 - 180/np.pi * aa) + 180, 360) - 180 for aa in (lang,rang)]
        # export these...
        for (h,nm,dat) in [('lh','eccen',lecc), ('lh','angle',lang),
                           ('rh','eccen',recc), ('rh','angle',rang)]:
            ny.save('%s.%s-%s.mgz' % (h, ds, nm), dat)
    # That's it!
    return 0