def _set_vol_space_cmd(fn, config): param = '' del_cmd = '' p, n, o, e = utils.afni_fileparts(fn) space = config.get('outvol_space', None) if space is not None: param += ' -space %s' % space view = config.get('outvol_view') if view is not None: param += ' -view %s' % view if len(o) and o[0] == '+' and o[1:].lower() != view.lower(): trgfn = '%s/%s+%s' % (p, n, view.lower()) del_cmd = 'rm %s*; ' % trgfn if param: p, n, o, e = utils.afni_fileparts(fn) cmd = 'cd %s; %s 3drefit -overwrite %s %s' % (p, del_cmd, param, n + o + e) else: cmd = 'echo "Not changing view or space for %s"' % fn return cmd
def _ext(config, for1D=False): '''Returns the extension for a file name''' if _is_surf(config): return '.1D.dset' if for1D else '.niml.dset' else: fn = config['data_files'][0] return ''.join(utils.afni_fileparts(fn)[2:])
def _convert_vol_space_to_orig_cmd(fn): p, n, o, e = utils.afni_fileparts(fn) if o == '+orig': return "echo '%s' is already in orig space - no conversion" % fn t = '__tmp_foo_' while os.path.exists(p + '/' + t + o + e): t += 'x' cmds = ['cd %s' % p, '3drename -overwrite %s%s %s%s' % (n, o, t, o), '3drefit -view orig %s%s' % (t, o), '3drename -overwrite %s+orig %s+orig' % (t, n)] return ';'.join(cmds)
def _remove_files(config, list_of_files): # removes a list of files, if config allows for it # in the case of AFNI volume files, it removes HEAD and BRIK files if not config['keep_files']: for fn in list_of_files: fp = utils.afni_fileparts(fn) if fp[2]: # AFNI HEAD/BRIK combination # ensure we delete all of them exts = ['.HEAD', '.BRIK', '.BRIK.gz'] fn = ''.join(fp[1:3]) else: exts = [''] for ext in exts: full_fn = os.path.join(config['output_dir'], fn + ext) if os.path.exists(full_fn): os.remove(full_fn)
def run_makesurfmasks(config, env): refdir = config['refdir'] overwrite = config['overwrite'] if config['sid'] is None: raise ValueError('Need sid') sumfn = '%s_qa_surf_mask' % config['sid'] # output file fullext = config['outvol_fullext'] volor = config['outvol_ext'] sumfn_path = '%s/%s%s' % (refdir, sumfn, fullext) qafn_path = '%s/%s.png' % (refdir, sumfn) checkfn_paths = (sumfn_path, qafn_path) if all(map(os.path.exists, checkfn_paths)) and not overwrite: print "Already exist: %s" % (", ".join(checkfn_paths)) return icolds, hemis = _get_hemis_icolds(config) volexts = ['%s%s' % (volor, e) for e in '.HEAD', '.BRIK*'] sssuffix = config['sssuffix'] if config['expvol_ss'] else '' expvol_fn = '%s%s%s' % (utils.afni_fileparts(config['expvol'])[1], sssuffix, volor) #if overwrite or not os.path.exists('%s/%s' % (refdir, sv_al_nii_fn)): # cmd = 'cd %s; 3dcopy -overwrite %s %s' % (refdir, sv_al_orig_fn, sv_al_nii_fn) # utils.run_cmds(cmd, env) if hemis != ['l', 'r']: raise ValueError("Cannot run without left and right hemisphere") icold = max(icolds) oneDfn = '__t.1D' oneDtfn = '__tt.1D' # transposed cmds = ['cd %s' % refdir, '1deval -1D: -num %d -expr 1 > %s' % (icold ** 2 * 10 + 1, oneDfn), '1dtranspose %s > %s' % (oneDfn, oneDtfn)] utils.run_cmds(';'.join(cmds), env) tmpfns = [oneDfn, oneDtfn] s2v_cmd = ('3dSurf2Vol -map_func mask2 -data_expr "a*%%d" -spec %%s %%s -sv %s' ' -grid_parent %s. -prefix %%s -sdata %s -overwrite') % \ (expvol_fn, expvol_fn, oneDtfn) infix2val = {'-surf_A pial':1, '-surf_A smoothwm':2, '-surf_A smoothwm -surf_B pial -f_steps 20': 4} volfns = [] for hemi in hemis: specfn = afni_suma_spec.canonical_filename(icold, hemi, config['alsuffix']) for infix, val in infix2val.iteritems(): fnprefix = '__m%d_%sh' % (val, hemi) cmd = s2v_cmd % (val, specfn, infix, fnprefix) utils.run_cmds('cd %s;%s' % (refdir, cmd)) tmpfns.extend(['%s%s' % (fnprefix, e) for e in volexts]) volfns.append(fnprefix + volor) cmds = ['cd %s' % refdir] catfn = '__cat' cmds.extend(['3dTcat -overwrite -prefix %s %s' % (catfn, ' '.join(volfns)), '3dTstat -overwrite -sum -prefix %s %s%s' % (sumfn, catfn, volor)]) tmpfns.extend(['%s%s' % (catfn, e) for e in volexts]) cmds.extend('rm %s' % fn for fn in tmpfns) cmds.append('echo "Surface mask in %s"' % sumfn) utils.run_cmds(';'.join(cmds), env) # make plot if overwrite or not os.path.exists(qafn_path): expvol_path = '%s/%s' % (refdir, expvol_fn) _make_slice_plot(expvol_path, sumfn_path, qafn_path)
def run_alignment(config, env): '''Aligns anat (which is assumed to be aligned with EPI data) to FreeSurfer SurfVol This function strips the anatomicals (by default), then uses align_epi_anat.py to estimate the alignment, then applies this transformation to the non-skull-stripped SurfVol and also to the surfaces. Some alignment headers will be nuked''' overwrite = config['overwrite'] alignsuffix = config['al2expsuffix'] refdir = config['refdir'] fullext = config['outvol_fullext'] ext = config['outvol_ext'] if config['sid'] is None: raise ValueError('Need sid') cmds = [] if not os.path.exists(config['refdir']): cmds.append('mkdir %(refdir)s' % config) # two volumes may have to be stripped: the inpput anatomical, and the surfvol. # put them in a list here and process them similarly surfvol = '%(refdir)s/%(sid)s_SurfVol%(outvol_fullext)s' % config surfvol_ss = '%(refdir)s/%(sid)s_SurfVol%(sssuffix)s%(outvol_fullext)s' % config e_p, e_n, _, _ = utils.afni_fileparts(config['expvol']) if config['expvol_ss']: e_n = '%s%s' % (e_n, config['sssuffix']) expvol = '%s/%s%s' % (refdir, e_n, fullext) volsin = [surfvol_ss, expvol] for volin in volsin: if not os.path.exists(volin): raise ValueError('File %s does not exist' % volin) a_n = utils.afni_fileparts(volsin[0])[1] # surfvol input root name ssalprefix = '%s%s' % (a_n, alignsuffix) unity = "1 0 0 0 0 1 0 0 0 0 1 0" # we all like unity, don't we? fullmatrixfn = '%s_mat.aff12.1D' % ssalprefix aloutfns = ['%s%s' % (ssalprefix, fullext), fullmatrixfn] # expected output files if alignment worked if config['overwrite'] or not all([os.path.exists('%s/%s' % (refdir, f)) for f in aloutfns]): alignedfn = '%s/%s%s' % (refdir, ssalprefix, fullext) if config['identity']: fullmatrix_content = '"MATRIX(%s)"' % unity.replace(" ", ",") cmd = 'cd "%s"; cat_matvec %s > %s; 3dcopy -overwrite %s%s %s%s%s' % (refdir, fullmatrix_content, fullmatrixfn, a_n, ext, a_n, alignsuffix, ext) else: # use different inputs depending on whether expvol is EPI or ANAT twovolpat = ('-anat %s -epi %s -anat2epi -epi_base 0 -anat_has_skull no -epi_strip None' if config['isepi'] else '-dset1 %s -dset2 %s -dset1to2 -dset1_strip None -dset2_strip None') # use this pattern to generate a suffix twovolsuffix = twovolpat % (volsin[0], volsin[1]) aea_opts = config['aea_opts'] if config['template']: aea_opts += " -Allineate_opts '-maxrot 10 -maxshf 10 -maxscl 1.5'" # align_epi_anat.py cmd = 'cd "%s"; align_epi_anat.py -overwrite -suffix %s %s %s' % (refdir, alignsuffix, twovolsuffix, aea_opts) cmds.append(cmd) cmds.append(_set_vol_space_cmd(alignedfn, config)) utils.run_cmds(cmds, env) else: print "Alignment already done - skipping" # run these commands first, then check if everything worked properly cmds = [] # see if the expected transformation file was found if not config['identity'] and not os.path.exists('%s/%s' % (refdir, fullmatrixfn)): raise Exception("Could not find %s in %s" % (fullmatrixfn, refdir)) # now make a 3x4 matrix matrixfn = '%s%s.A2E.1D' % (a_n, alignsuffix) if overwrite or not os.path.exists('%s/%s' % (refdir, matrixfn)): cmds.append('cd "%s"; cat_matvec %s > %s || exit 1' % (refdir, fullmatrixfn, matrixfn)) # make an aligned, non-skullstripped version of SurfVol in refdir alprefix = '%s_SurfVol%s' % (config['sid'], alignsuffix) svalignedfn = '%s/%s%s' % (refdir, alprefix, fullext) newgrid = 1 # size of anatomical grid in mm. We'll have to resample, otherwise 3dWarp does # not respect the corners of the volume (as of April 2012) if overwrite or not os.path.exists(svalignedfn): #if not config['fs_sid']: # raise ValueError("Don't have a freesurfer subject id - cannot continue") #surfvolfn = '%s/%s_SurfVol+orig' % (config['sumadir'], config['fs_sid']) surfvolfn = '%s/T1.nii' % config['sumadir'] cmds.append('cd "%s";3dWarp -overwrite -newgrid %f -matvec_out2in `cat_matvec -MATRIX %s` -prefix ./%s %s' % (refdir, newgrid, matrixfn, alprefix, surfvolfn)) cmds.append(_set_vol_space_cmd('%s/%s+orig' % (refdir, alprefix), config)) else: print '%s already exists - skipping Warp' % svalignedfn utils.run_cmds(cmds, env) cmds = [] # nuke afni headers headernukefns = ['%s%s' % (f, fullext) for f in [ssalprefix, alprefix]] headernukefields = ['ALLINEATE_MATVEC_B2S_000000', 'ALLINEATE_MATVEC_S2B_000000', 'WARPDRIVE_MATVEC_FOR_000000', 'WARPDRIVE_MATVEC_INV_000000'] for fn in headernukefns: for field in headernukefields: # nuke transformation - otherwise AFNI does this unwanted transformation for us fullfn = '%s/%s' % (refdir, fn) if not (os.path.exists(fullfn) or config['identity']): raise ValueError("File %r does not exist" % fullfn) refitcmd = "3drefit -atrfloat %s '%s' %s" % (field, unity, fn) # only refit if not already in AFNI history (which is stored in HEADfile) cmd = 'cd "%s"; m=`grep "%s" %s | wc -w`; if [ $m -eq 0 ]; then %s; else echo "File %s seems already 3drefitted"; fi' % (refdir, refitcmd, fn, refitcmd, fn) cmds.append(cmd) utils.run_cmds('; '.join(cmds), env) cmds = [] # run AddEdge so that volumes can be inspected visually for alignment if config['AddEdge']: use_ss = config['expvol_ss'] # ae_{e,s}_n are AddEdge names for expvol and surfvol ae_e_n = utils.afni_fileparts(config['expvol'])[1] if use_ss: ae_e_n += config['sssuffix'] ae_s_n = ssalprefix #if use_ss else alprefix # *_ne have the output extension as well ae_e_ne = ae_e_n + ext ae_s_ne = ae_s_n + ext addedge_fns = ['%s/_ae.ExamineList.log' % refdir] exts = ['HEAD', 'BRIK'] orig_ext = '+orig' addedge_rootfns = ['%s_%s%%s' % (ae_e_n, postfix) for postfix in ['e3', 'ec', ae_s_n + '_ec']] addedge_rootfns.extend(['%s_%s%%s' % (ae_s_n, postfix) for postfix in ['e3', 'ec']]) addedge_fns_pat = ['%s.%s' % (fn, e) for fn in addedge_rootfns for e in exts] addegde_pathfns_orig = map(lambda x:os.path.join(refdir, x % '+orig'), addedge_fns_pat) + addedge_fns addegde_pathfns_ext = map(lambda x:os.path.join(refdir, x % ext), addedge_fns_pat) addegde_exists = map(os.path.exists, addegde_pathfns_ext) if overwrite or not all(addegde_exists): ae_ns = (ae_e_n, ae_s_n) cmds.extend(map(lambda fn : 'if [ -e "%s" ]; then rm "%s"; fi' % (fn, fn), addegde_pathfns_orig + addegde_pathfns_ext)) cmds.append(';'.join(['cd %s' % refdir] + [_convert_vol_space_to_orig_cmd('%s/%s%s' % (refdir, n, ext)) for n in ae_ns] + ['\@AddEdge %s+orig %s+orig' % ae_ns])) set_space_fns = addegde_pathfns_orig + ['%s/%s%s.%s' % (refdir, fn, orig_ext, exts[0]) for fn in ae_ns] for fn in set_space_fns: #['%s/%s' % (refdir, fn % orig_ext) for fn in addedge_fns_pat]: if fn.endswith('.log'): continue cmds.append('if [ -e %s ]; then %s; fi' % (fn, _set_vol_space_cmd(fn, config))) utils.run_cmds(cmds, env) cmds = [] else: print "AddEdge seems to have been run already" sid = config['sid'] plot_slice_fns = [(ae_e_n + '_e3', ae_s_n + '_e3', '%s_qa_e3.png' % sid), (None, ae_e_n + '_' + ae_s_n + '_ec', '%s_qa_ec.png' % sid)] plot_slice_imgfns = ['%s/%s' % (refdir, fn) for fn in plot_slice_fns] if overwrite or not all(map(os.path.exists, plot_slice_imgfns)): slice_dims = [0, 1, 2] slice_pos = [.35, .45, .55, .65] for fns in plot_slice_fns: input_fns = [] for i, fn in enumerate(fns): if fn is not None: fn = '%s/%s' % (refdir, fn) if i <= 1: fn += ext input_fns.append(fn) fn1, fn2, fnout = input_fns if not os.path.exists(fnout): _make_slice_plot(fn1, fn2, fnout) print "QA Image saved to %s" % fnout else: print "Already exists: %s" % fnout else: print "QA images already exist" # because AFNI uses RAI orientation but FreeSurfer LPI, make a new # affine transformation matrix in which the signs of # x and y coordinates are negated before and after the transformation matrixfn_LPI2RAI = '%s.A2E_LPI.1D' % ssalprefix if overwrite or not os.path.exists('%s/%s' % (refdir, matrixfn_LPI2RAI)): lpirai = '"MATRIX(-1,0,0,0,0,-1,0,0,0,0,1,0)"' cmd = ('cd %s; cat_matvec -ONELINE %s `cat_matvec -MATRIX %s` %s > %s' % (refdir, lpirai, matrixfn, lpirai, matrixfn_LPI2RAI)) cmds.append(cmd) # apply transformation to surfaces [icolds, hemis] = _get_hemis_icolds(config) sumadir = config['sumadir'] sumafiles = os.listdir(sumadir) origext = '.asc' ext = format2extension(config) tp = format2type(config) # process all hemispheres and ld values for icold in icolds: for hemi in hemis: pat = '%s%sh.?*%s' % (config['mi_icopat'] % icold, hemi, origext) for sumafile in sumafiles: if fnmatch.fnmatch(sumafile, pat): if not sumafile.endswith(origext): raise ValueError("%s does not end with %s" % (sumafile, origext)) #s = sumafile.split(".") #s[len(s) - 2] += config['alsuffix'] # insert '_al' just before last dot #alsumafile = ".".join(s) extsumafile = sumafile[:-len(origext)] alsumafile = extsumafile + config['alsuffix'] + ext if config['overwrite'] or not os.path.exists('%s/%s' % (refdir, alsumafile)): # now apply transformation cmd = 'cd "%s";ConvertSurface -overwrite -i_fs %s/%s -o_%s ./%s -ixmat_1D %s' % \ (refdir, sumadir, sumafile, tp, alsumafile, matrixfn_LPI2RAI) cmds.append(cmd) # as of June 2012 copy the original sphere.reg (not aligned) as well if sumafile == ('%s.sphere.reg%s' % (pat, ext)): sumaout = '%s/%s' % (refdir, extsumafile + ext) if config['overwrite'] or not os.path.exists(sumaout): s = surf.read('%s/%s' % (sumadir, sumafile)) surf.write(s, sumaout) #cmds.append('cp %s/%s %s/%s' % (sumadir, sumafile, refdir, sumafile)) mapfn = (config['mi_icopat'] % icold) + config['hemimappingsuffix'] srcpathfn = os.path.join(sumadir, mapfn) if os.path.exists(srcpathfn): trgpathfn = os.path.join(refdir, mapfn) if not os.path.exists(trgpathfn) or config['overwrite']: cmds.append('cp %s %s' % (srcpathfn, trgpathfn)) utils.run_cmds(cmds, env)
def run_skullstrip(config, env): fullext = config['outvol_fullext'] overwrite = config['overwrite'] refdir = config['refdir'] cmds = [] if not os.path.exists(refdir): cmds.append('mkdir %(refdir)s' % config) sumadir = config['sumadir'] sid = config['sid'] fs_sid = config['fs_sid'] if not sid: raise ValueError("Subject id is not set, cannot continue") # process the surfvol anatomical. # because it's already skull stripped by freesurfer # simply copy it over; rename brain.nii to surfvol_ss surfvol_srcs = ['%s/%s' % (sumadir, fn) for fn in ['brain.nii', 'T1.nii']] surfvol_trgs = ['%s/%s' % (refdir, fn) for fn in ['%s_SurfVol_ss%s' % (sid, fullext), '%s_SurfVol%s' % (sid, fullext)]] for src, trg in zip(surfvol_srcs, surfvol_trgs): if os.path.exists(trg) and not overwrite: print '%s already exists' % trg else: t_p, t_n, t_o, t_e = utils.afni_fileparts(trg) trg_short = '%s%s' % (t_n, t_o) cmds.append('cd "%s"; 3dresample -overwrite -orient LPI -inset %s -prefix ./%s' % (refdir, src, trg_short)) cmds.append(_set_vol_space_cmd('%s/%s+orig' % (refdir, t_n), config)) # process experimental volume. expvol_src = config['expvol'] do_ss = config['expvol_ss'] [e_p, e_n, e_o, e_e] = utils.afni_fileparts(expvol_src) expvol_trg_prefix = '%s%s' % (e_n, config['sssuffix'] if do_ss else '') expvol_trg_tmp_prefix = '__tmp_%s' % expvol_trg_prefix expvol_trg = '%s/%s%s' % (refdir, expvol_trg_prefix, fullext) print "Attempt %s -> %s" % (expvol_src, expvol_trg) ext = config['outvol_ext'] if overwrite or not utils.afni_fileexists(expvol_trg): if do_ss: cmds.append('cd "%s";3dSkullStrip -overwrite -prefix ./%s%s -input %s' % (refdir, expvol_trg_tmp_prefix, ext, expvol_src)) else: cmds.append('cd "%s";3dbucket -overwrite -prefix ./%s%s %s' % (refdir, expvol_trg_tmp_prefix, ext, expvol_src)) cmds.append('cd "%s"; 3dresample -overwrite -orient LPI -prefix %s -inset %s%s' % (refdir, expvol_trg_prefix, expvol_trg_tmp_prefix, ext)) cmds.append('rm %s/%s*' % (refdir, expvol_trg_tmp_prefix)) cmds.append(_set_vol_space_cmd(expvol_trg, config)) else: print "No skull strip because already exists: %s%s" % (expvol_trg_prefix, ext) utils.run_cmds(cmds, env)
def augmentconfig(c): '''add more configuration values that are *derived* from the current configuration, and also checks whether some options are set properly''' # ensure surfdir is set properly. # normally it should end in 'surf' # try to be smart and deduce subject id from surfdir (if not set explicitly) surfdir = c['surfdir'] if surfdir: surfdir = os.path.abspath(surfdir) parent, nm = os.path.split(surfdir) if nm != 'surf': jn = os.path.join(surfdir, 'surf') if os.path.exists(jn): surfdir = jn elif nm == 'SUMA': surfdir = parent if not (os.path.exists(surfdir) or os.path.split(surfdir)[1] == 'surf'): print('Warning: surface directory %s not does exist or does not end in "surf"' % surfdir) surfdir = None c['surfdir'] = surfdir # set SUMADIR as well c['sumadir'] = '%(surfdir)s/SUMA/' % c # derive subject id from surfdir sid = os.path.split(os.path.split(surfdir)[0])[1] if surfdir else None if c.get('sid') is None: c['sid'] = sid if c['sid'] is None: print"Warning: no subject id specified" c['prefix_sv2anat'] = 'SurfVol2anat' # update steps if c.get('steps', 'all') == 'all': c['steps'] = 'toafni+mapico+moresurfs+skullstrip+align+makespec+makespecboth+makesurfmasks' hasanatvol = 'anatvol' in c and c['anatvol'] hasepivol = 'epivol' in c and c['epivol'] hasexpvol = 'expvol' in c and c['expvol'] hasisepi = 'isepi' in c if hasexpvol: if hasanatvol or hasepivol: raise Exception("expvol specified, but also anatvol or epivol - illegal!") if not hasisepi: raise Exception("not specified whether expvol is EPI (yes) or anat (no)") else: if hasanatvol: if 'epivol' in c and c['epivol']: raise Exception("Cannot have both anatvol and epivol") else: c['expvol'] = c['anatvol'] c['isepi'] = False del(c['anatvol']) else: if hasepivol: c['expvol'] = c['epivol'] c['isepi'] = True del(c['epivol']) else: print("Warning: no anatomical or functional experimental voume defined") def yesno2bool(d, k): # dict, key '''Assuming d[k] contains the word 'yes' or 'no', makes d[k] a boolean (True=='yes',False=='no'); otherwise an exception is thrown. The dict is updated''' val = d[k] if val is None: b = False elif type(val) == bool: b = val else: v = val.lower() if v == 'yes': b = True elif v == 'no': b = False else: raise Exception("Not yes or no: %s" % val) d[k] = b yesno2bool(c, 'AddEdge') if c['identity']: c['expvol_ss'] = c['anatval_ss'] = False else: yesno2bool(c, 'expvol_ss') yesno2bool(c, 'isepi') # see if we can get the fs_sid # (only if surfdir is set properly) # XXX not sure if this still makes sense c['fs_sid'] = None surfdir = c.get('surfdir', None) if not surfdir is None and os.path.exists(surfdir): fs_log_fn = os.path.join(surfdir, '..', 'scripts', 'recon-all.done') print "Looking in %s" % fs_log_fn if os.path.exists(fs_log_fn): with open(fs_log_fn) as f: lines = f.read().split('\n') for line in lines: if line.startswith('SUBJECT'): fs_sid = line[8:] c['fs_sid'] = fs_sid print "Found Freesurfer sid %s" % fs_sid break if c['fs_sid'] is None: c['fs_sid'] = sid print "Unable to find proper Freesurfer sid" pathvars = ['anatvol', 'expvol', 'epivol', 'refdir', 'surfdir'] for pathvar in pathvars: if pathvar in c and c[pathvar]: c[pathvar] = os.path.abspath(c[pathvar]) print "Set absolute path for %s: %s" % (pathvar, c[pathvar]) if c['template'] and c['notemplate']: error('Cannot have both template and notemplate') if 'expvol' in c: p, n, o, e = utils.afni_fileparts(c['expvol']) if c.get('outvol_space', None) is None: cmd = '3dAttribute TEMPLATE_SPACE %s' % c['expvol'] outvol_space = utils.cmd_capture_output(cmd) outvol_space = outvol_space.split('~')[0].strip() if len(outvol_space) and not c['notemplate'] and outvol_space.lower() != 'orig': print "Detected TEMPLATE_SPACE=%s" % outvol_space c['outvol_space'] = outvol_space if o == '+orig': o = '+tlrc' print "Template space '%s' detected: output has extension %s" % (outvol_space, o) c['template'] = True else: c['outvol_space'] = '+orig' if len(o): #'+orig' or '+tlrc' c['outvol_ext'] = o c['outvol_fullext'] = o + e c['outvol_view'] = o[1:] else: # For NIFTI - output in orig or tlrc c['outvol_view'] = 'tlrc' if c['template'] else 'orig' c['outvol_ext'] = '+' + c['outvol_view'] c['outvol_fullext'] = c['outvol_ext'] + '.HEAD' return c