def asegstats2table(fsdir, output_directory=None, fsconfig="/i2bm/local/freesurfer/SetUpFreeSurfer.sh"): """ Generate text/ascii tables of freesurfer parcellation stats data 'aseg.stats'. This can then be easily imported into a spreadsheet and/or stats program. The labels are located here: $FREESURFER_HOME/FreeSurferColorLUT.txt <unit> <input name="fsdir" type="Directory" description="The freesurfer working directory with all the subjects."/> <input name="output_directory" type="Directory" description="The statistical destination folder."/> <input name="fsconfig" type="File" description="The freesurfer configuration batch."/> <output name="statfiles" type="List_File" description="The freesurfer summary stats."/> </unit> """ # Parameter that will contain the output stats statfiles = [] # Fist find all the subjects with a stat dir statdirs = glob.glob(os.path.join(fsdir, "*", "stats")) subjects = [item.lstrip(os.sep).split("/")[-2] for item in statdirs] if output_directory is not None: path = os.path.join(output_directory, "subjects.json") with open(path, "w") as open_file: json.dump(subjects, open_file, indent=4) # Save the FreeSurfer current working directory and set the new one fscwd = None if "SUBJECTS_DIR" in os.environ: fscwd = os.environ["SUBJECTS_DIR"] os.environ["SUBJECTS_DIR"] = fsdir # Create the output stat directory fsoutdir = os.path.join(fsdir, "stats") if not os.path.isdir(fsoutdir): os.makedirs(fsoutdir) # Call freesurfer statfile = os.path.join(fsoutdir, "aseg_stats_volume.csv") statfiles.append(statfile) cmd = ["asegstats2table", "--subjects"] + subjects + [ "--meas", "volume", "--tablefile", statfile, "--delimiter", "comma" ] recon = FSWrapper(cmd, shfile=fsconfig) recon() if recon.exitcode != 0: raise FreeSurferRuntimeError(recon.cmd[0], " ".join(recon.cmd[1:]), recon.stderr + recon.stdout) # Restore the FreeSurfer working directory if fscwd is not None: os.environ["SUBJECTS_DIR"] = fscwd return statfiles
def mri_surf2surf(hemi, input_surface_file, output_surface_file, ico_order, fsdir, sid, fsconfig="/i2bm/local/freesurfer/SetUpFreeSurfer.sh"): """ Resample surface vertices. Wrapper around the FreeSurfer 'mri_surf2surf' command to create the described texture. Parameters ---------- hemi: str (mandatory) hemisphere ('lh' or 'rh'). input_surface_file: str (mandatory) input surface path. output_surface_file: str (mandatory) output '.mgz' surface path. ico_order: int (mandatory) icosahedron order in [0, 7] that will be used to generate the cortical surface texture at a specific tessalation (the corresponding cortical surface can be resampled using the 'clindmri.segmentation.freesurfer.resample_cortical_surface' function). fsdir: str (mandatory) FreeSurfer subjects directory 'SUBJECTS_DIR'. sid: str (mandatory) FreeSurfer subject identifier. fsconfig: str (optional) The FreeSurfer '.sh' config file. """ # Check input parameters if hemi not in ["lh", "rh"]: raise ValueError("'{0}' is not a valid hemisphere value which must be " "in ['lh', 'rf']".format(hemi)) if ico_order < 0 or ico_order > 7: raise ValueError("'Ico order '{0}' is not in 0-7 " "range.".format(ico_order)) # Set the output surface extension if necessary if not output_surface_file.endswith(".mgz"): output_surface_file += ".mgz" # Define FreeSurfer command cmd = [ "mri_surf2surf", "--hemi", hemi, "--srcsurfval", input_surface_file, "--srcsubject", sid, "--trgsubject", "ico", "--trgicoorder", str(ico_order), "--trgsurfval", output_surface_file, "--sd", fsdir, "--trg_type", "mgz" ] # Execute the FS command recon = FSWrapper(cmd, shfile=fsconfig) recon() if recon.exitcode != 0: raise FreeSurferRuntimeError(recon.cmd[0], " ".join(recon.cmd[1:]), recon.stderr + recon.stdout)
def mri_binarize(inputfile, outputfile, match=[], wm=False, fsconfig="/i2bm/local/freesurfer/SetUpFreeSurfer.sh"): """ Binarize a FreeSurfer label map. USAGE: mri_binarize --i invol : input volume --min min : min thresh (def is -inf) --max max : max thresh (def is +inf) --pct P : set threshold to capture top P% (in mask or total volume) --rmin rmin : compute min based on rmin*globalmean --rmax rmax : compute max based on rmax*globalmean --match matchval <matchval2 ...> : match instead of threshold --wm : set match vals to 2 and 41 (aseg for cerebral WM) --ventricles : set match vals those for aseg ventricles+choroid (not 4th) --wm+vcsf : WM and ventricular CSF, including choroid (not 4th) --o outvol : output volume --count countfile : save number of hits in ascii file (hits,ntotvox,pct) --binval val : set vox within thresh to val (default is 1) --binvalnot notval : set vox outside range to notval (default is 0) --inv : set binval=0, binvalnot=1 --frame frameno : use 0-based frame of input (default is 0) --frame-sum : sum frames together before binarizing --frame-and : take intersection (AND) of frames. No --min needed. --merge mergevol : merge with mergevolume --mask maskvol : must be within mask --mask-thresh thresh : set thresh for mask (def is 0.5) --abs : take abs of invol first (ie, make unsigned) --bincol : set binarized voxel value to its column number --zero-edges : zero the edge voxels --zero-slice-edges : zero the edge slice voxels --dilate ndilate: dilate binarization in 3D --erode nerode: erode binarization in 3D (after any dilation) --erode2d nerode2d: erode binarization in 2D (after any 3D erosion) --debug turn on debugging --checkopts don't run anything, just check options and exit --help print out information on how to use this program --version print out version and exit """ # Call freesurfer cmd = ["mri_binarize", "--i", inputfile, "--o", outputfile] if len(match) > 0: cmd.append("--match") cmd.extend(match) if wm: cmd.append("--wm") recon = FSWrapper(cmd, shfile=fsconfig) recon() if recon.exitcode != 0: raise FreeSurferRuntimeError(recon.cmd[0], " ".join(recon.cmd[1:]), recon.stderr + recon.stdout)
def run_freesurfer_cmd(cmd, subjects_dir=None, add_fsl_env=False, fsl_init="/etc/fsl/5.0/fsl.sh"): """ To avoid repeating the code to run Freesurfer and check exitcode everywhere. Step: - add $SUBJECTS_DIR to the environment if requested - add FSL's environment if requested (some Freesurfer commands require FSL) - run the Freesurfer cmd - check exit code Parameters ---------- cmd: list of str the command to run (subprocess like). subjects_dir: str, default None. To set the $SUBJECTS_DIR environment variable. add_fsl_env: bool, default False To activate the FSL environment, required for commands like bbregister. fsl_init: str Path to the Bash script setting the FSL environment, if needed. """ fsprocess = FSWrapper(cmd) if subjects_dir is not None: fsprocess.environment["SUBJECTS_DIR"] = subjects_dir if add_fsl_env: fsl_env = environment(fsl_init) for k, i in fsl_env.items(): if k not in fsprocess.environment: fsprocess.environment[k] = i else: # A variable that exists in both FS and FSL environments if k == "PATH": fsprocess.environment["PATH"] += ":" + fsl_env["PATH"] else: pass # ignore this variable fsprocess() # Run if fsprocess.exitcode != 0: raise FreeSurferRuntimeError(cmd[0], " ".join(cmd[1:])) return fsprocess
def conformed_to_native_space( fsdir, output_directory, fsconfig="/i2bm/local/freesurfer/SetUpFreeSurfer.sh"): """ Create a registration matrix between the conformed space (orig.mgz) and the native anatomical (rawavg.mgz). <unit> <input name="fsdir" type="Directory" description="The freesurfer working directory with all the subjects."/> <input name="output_directory" type="Directory" description="The default resample destination folder."/> <input name="fsconfig" type="File" description="The freesurfer configuration batch."/> <output name="trffiles" type="List_File" description="The conformed to native transformation files."/> </unit> """ # Get all the subjects with a 'mri' directory mridirs = glob.glob(os.path.join(fsdir, "*", "mri")) with open(os.path.join(output_directory, "mris.json"), "w") as open_file: json.dump(mridirs, open_file, indent=4) # Go through all the subjects with the desired folder trffiles = [] for mdir in mridirs: # Get some information based on the folder path subject_id = mdir.rstrip("/").split("/")[-2] convertdir = os.path.join(fsdir, subject_id, "convert") if not os.path.isdir(convertdir): os.makedirs(convertdir) # Check that the two images of interest are present rawfile = os.path.join(mdir, "rawavg.mgz") origfile = os.path.join(mdir, "orig.mgz") if not (os.path.isfile(rawfile) and os.path.isfile(origfile)): raise ValueError("In folder '{0}' can't find file '{1}' or file " "'{2}'.".format(mdir, rawfile, origfile)) # Construct the FS command trffile = os.path.join(convertdir, "register.native.dat") trffiles.append(trffile) cmd = [ "tkregister2", "--mov", rawfile, "--targ", origfile, "--reg", trffile, "--noedit", "--regheader" ] # Execute the FS command recon = FSWrapper(cmd, shfile=fsconfig) recon() if recon.exitcode != 0: raise FreeSurferRuntimeError(recon.cmd[0], " ".join(recon.cmd[1:]), recon.stderr) return trffiles
def register_diffusion_to_anatomy(outdir, nodif_brain, subject_id, fs_subjects_dir = None, subdir = "diff_to_anat", fsl_init = "/etc/fsl/5.0/fsl.sh"): """ Register the diffusion to the anatomy (T1) using Freesurfer bbregister (boundary-based registration). <unit> <input name="outdir" type="Directory" /> <input name="nodif_brain" type="File" /> <input name="subject_id" type="Str" /> <input name="fs_subjects_dir" type="Directory" /> <input name="subdir" type="Str" /> <input name="fsl_init" type="File" /> <output name="dif2anat_dat" type="File" description=" The .dat file created by tkregister/bbregister cmd." /> </unit> """ if fs_subjects_dir is None: if "SUBJECTS_DIR" in os.environ: fs_subjects_dir = os.environ["SUBJECTS_DIR"] else: raise ValueError("Missing <SUBJECTS_DIR>: set the $SUBJECTS_DIR " "environment variable for Freesurfer or pass it " "as an argument.") # If a subdirectory name has been passed, adapt outdir if subdir: outdir = os.path.join(outdir, subdir) # Create outdir if it does not exist if not os.path.isdir(outdir): os.makedirs(outdir) dif2anat_dat = os.path.join(outdir, "dif2anat.dat") cmd = ["bbregister", "--s", subject_id, "--mov", nodif_brain, "--reg", dif2anat_dat, "--dti", "--init-fsl"] fsprocess = FSWrapper(cmd) fsprocess.environment["SUBJECTS_DIR"] = fs_subjects_dir # bbregister requires FSL, so we have to add the FSL environment fsl_env = environment(fsl_init) for k, i in fsl_env.items(): if k not in fsprocess.environment: fsprocess.environment[k] = i else: # A variable that exists in both FS and FSL environments if k == "PATH": fsprocess.environment["PATH"] += ":" + fsl_env["PATH"] else: pass # ignore this variable fsprocess() # Run if fsprocess.exitcode != 0: raise FreeSurferRuntimeError(cmd[0], " ".join(cmd[1:])) return dif2anat_dat
def resample_cortical_surface( fsdir, regex, output_directory=None, orders=[4, 5, 6, 7], surface_name="white", fsconfig="/i2bm/local/freesurfer/SetUpFreeSurfer.sh"): """ Resamples one cortical surface onto an icosahedron. Resample the white or pial FreeSurfer cotical surface using the 'mri_surf2surf' command. Map also the associated annotation file. Can resample at different icosahedron order which specifies the size of the icosahedron according to the following table: Order Number of Vertices 0 12 1 42 2 162 3 642 4 2562 5 10242 6 40962 7 163842 <unit> <input name="fsdir" type="Directory" description="The freesurfer working directory with all the subjects."/> <input name="regex" type="String" description="A regular expression used to locate the surface files to be converted from the 'fsdir' directory."/> <input name="output_directory" type="Directory" description="The default resample destination folder."/> <input name="orders" type="List_Int" description="The icosahedron orders."/> <input name="surface_name" type="String" description="The surface we want to resample ('white' or 'pial')."/> <input name="fsconfig" type="File" description="The freesurfer configuration batch."/> <output name="resamplefiles" type="List_File" description="The resample surfaces."/> <output name="annotfiles" type="List_File" description="The resample annotations."/> </unit> """ # Check input parameters if surface_name not in ["white", "pial"]: raise ValueError("'{0}' is not a valid surface value which must be in " "['white', 'pial']".format(surface_name)) norders = numpy.asarray(orders) if norders.min() < 0 or norders.max() > 7: raise ValueError("'At least one value in {0} is not in 0-7 " "range.".format(orders)) # Get all the subjects with the specified surface surfaces = glob.glob(os.path.join(fsdir, regex)) if output_directory is not None: path = os.path.join(output_directory, "surfaces.json") with open(path, "w") as open_file: json.dump(surfaces, open_file, indent=4) # Go through all the subjects with the desired surface resamplefiles = [] annotfiles = [] for surf in surfaces: # Get some information based on the surface path subject_id = surf.split("/")[-3] hemi = os.path.basename(surf).split(".")[0] convertdir = os.path.join(fsdir, subject_id, "convert") if not os.path.isdir(convertdir): os.makedirs(convertdir) # Go through all specified orders for level in orders: # Construct the FS surface map command convertfile = os.path.join( convertdir, "{0}.{1}.{2}".format(hemi, surface_name, level)) resamplefiles.append(convertfile) cmd = [ "mri_surf2surf", "--sval-xyz", surface_name, "--srcsubject", subject_id, "--trgsubject", "ico", "--trgicoorder", str(level), "--tval", convertfile, "--tval-xyz", "--hemi", hemi, "--sd", fsdir ] # Execute the FS command recon = FSWrapper(cmd, shfile=fsconfig) recon() if recon.exitcode != 0: raise FreeSurferRuntimeError(recon.cmd[0], " ".join(recon.cmd[1:]), recon.stderr + recon.stdout) # Construct the FS label map command annotfile = os.path.join(convertdir, "{0}.aparc.annot.{1}".format(hemi, level)) annotfiles.append(annotfile) if not os.path.isfile(annotfile): svalannot = os.path.join(fsdir, subject_id, "label", "{0}.aparc.annot".format(hemi)) cmd = [ "mri_surf2surf", "--srcsubject", subject_id, "--trgsubject", "ico", "--trgicoorder", str(level), "--hemi", hemi, "--sval-annot", svalannot, "--tval", annotfile, "--sd", fsdir ] # Execute the FS command recon = FSWrapper(cmd, shfile=fsconfig) recon() if recon.exitcode != 0: raise FreeSurferRuntimeError(recon.cmd[0], " ".join(recon.cmd[1:]), recon.stderr + recon.stdout) # Remove duplicate annotation files annotfiles = list(set(annotfiles)) return resamplefiles, annotfiles
def mri_vol2surf(hemi, volume_file, texture_file, ico_order, dat_file, fsdir, sid, surface_name="white", fsconfig="/i2bm/local/freesurfer/SetUpFreeSurfer.sh"): """ Assigns values from a volume to each surface vertex. Wrapper around the FreeSurfer 'mri_vol2surf' command to create the described texture. Parameters ---------- hemi: str (mandatory) hemisphere ('lh' or 'rh'). volume_file: str (mandatory) input volume path. texture_file: str (mandatory) output texture file. ico_order: int (mandatory) icosahedron order in [0, 7] that will be used to generate the cortical surface texture at a specific tessalation (the corresponding cortical surface can be resampled using the 'clindmri.segmentation.freesurfer.resample_cortical_surface' function). dat_file: str (mandatory) structural to FreeSurfer space affine transformation matrix '.dat' file as computed by 'tkregister2'. fsdir: str (mandatory) FreeSurfer subjects directory 'SUBJECTS_DIR'. sid: str (mandatory) FreeSurfer subject identifier. surface_name: str (optional, default 'white') The surface we want to resample ('white' or 'pial'). fsconfig: str (optional) The FreeSurfer '.sh' config file. """ # Check input parameters if hemi not in ["lh", "rh"]: raise ValueError("'{0}' is not a valid hemisphere value which must be " "in ['lh', 'rf']".format(hemi)) if surface_name not in ["white", "pial"]: raise ValueError("'{0}' is not a valid surface value which must be in " "['white', 'pial']".format(surface_name)) if ico_order < 0 or ico_order > 7: raise ValueError("'Ico order '{0}' is not in 0-7 " "range.".format(ico_order)) # Construct the FS vol2surf command cmd = [ "mri_vol2surf", "--src", volume_file, "--out", texture_file, "--srcreg", dat_file, "--hemi", hemi, "--trgsubject", "ico", "--icoorder", "{0}".format(ico_order), "--surf", surface_name, "--sd", fsdir, "--srcsubject", sid, "--noreshape", "--out_type", "mgz" ] # Execute the FS command recon = FSWrapper(cmd, shfile=fsconfig) recon() if recon.exitcode != 0: raise FreeSurferRuntimeError(recon.cmd[0], " ".join(recon.cmd[1:]), recon.stderr + recon.stdout)
def mri_convert(fsdir, regex, output_directory=None, reslice=True, interpolation="interpolate", fsconfig="/i2bm/local/freesurfer/SetUpFreeSurfer.sh"): """ Export Freesurfer "*.mgz" image in Nifti format. Convert in native space: the destination image is resliced like the 'rawavg.mgz' file if the reslice option is set. The converted file will then have a '.native' suffix. <unit> <input name="fsdir" type="Directory" description="The freesurfer working directory with all the subjects."/> <input name="regex" type="String" description="A regular expression used to locate the files to be converted from the 'fsdir' directory."/> <input name="output_directory" type="Directory" description="The conversion destination folder."/> <input name="reslice" type="Bool" description="If true reslice the input images like the raw image."/> <input name="interpolation" type="String" description="The interpolation method: interpolate|weighted|nearest|cubic."/> <input name="fsconfig" type="File" description="The freesurfer configuration batch."/> <output name="niftifiles" type="List_File" description="The converted nifti files."/> </unit> """ # Check the interpolation method if interpolation not in ["interpolate", "weighted", "nearest", "cubic"]: raise ValueError( "'{0}' is not a valid interpolation method.".format(interpolation)) # Get the images to convert from the regex inputs = glob.glob(os.path.join(fsdir, regex)) if output_directory is not None: path = os.path.join(output_directory, "inputs.json") with open(path, "w") as open_file: json.dump(inputs, open_file, indent=4) # Convert each input file niftifiles = [] for inputfile in inputs: # Create the output directory subject = inputfile.replace(fsdir, "") subject = subject.lstrip(os.sep).split(os.sep)[0] outdir = os.path.join(fsdir, subject, "convert") if not os.path.isdir(outdir): os.makedirs(outdir) # Create the FS command basename = os.path.basename(inputfile).replace(".mgz", "") cmd = ["mri_convert", "--resample_type", interpolation] # "--out_orientation", "RAS"] if reslice: reference_file = os.path.join(fsdir, subject, "mri", "rawavg.mgz") if not os.path.isfile(reference_file): raise ValueError("'{0}' does not exists, can't reslice image " "'{1}'.".format(reference_file, inputfile)) cmd += ["--reslice_like", reference_file] basename = basename + ".native" converted_file = os.path.join(outdir, basename + ".nii.gz") niftifiles.append(converted_file) cmd += [inputfile, converted_file] # Execute the FS command recon = FSWrapper(cmd, shfile=fsconfig) recon() if recon.exitcode != 0: raise FreeSurferRuntimeError(recon.cmd[0], " ".join(recon.cmd[1:]), recon.stderr + recon.stdout) return niftifiles
def recon_all(fsdir, anatfile, sid, output_directory=None, fsconfig="/i2bm/local/freesurfer/SetUpFreeSurfer.sh"): """ Performs all the FreeSurfer cortical reconstruction process. Processing stages: * Motion Correction and Conform * NU (Non-Uniform intensity normalization) * Talairach transform computation * Intensity Normalization 1 * Skull Strip * EM Register (linear volumetric registration) * CA Intensity Normalization * CA Non-linear Volumetric Registration * Remove Neck * LTA with Skull * CA Label (Volumetric Labeling, ie Aseg) and Statistics * Intensity Normalization 2 (start here for control points) * White matter segmentation * Edit WM With ASeg * Fill (start here for wm edits) * Tessellation (begins per-hemisphere operations) * Smooth1 * Inflate1 * QSphere * Automatic Topology Fixer * Final Surfs (start here for brain edits for pial surf) * Smooth2 * Inflate2 * Spherical Mapping * Spherical Registration * Spherical Registration, Contralateral hemisphere * Map average curvature to subject * Cortical Parcellation - Desikan_Killiany and Christophe (Labeling) * Cortical Parcellation Statistics * Cortical Ribbon Mask * Cortical Parcellation mapping to Aseg <unit> <input name="fsdir" type="Directory" description="The freesurfer working directory with all the subjects."/> <input name="anatfile" type="File" desc="The input anatomical image to be segmented with freesurfer."/> <input name="sid" type="Str" description="The current subject identifier."/> <input name="output_directory" type="Directory" description="The freesurfer runtime folder."/> <input name="fsconfig" type="File" description="The freesurfer configuration batch."/> <output name="subjfsdir" type="Directory" description="Path to the resulting freesurfer segmentation."/> </unit> """ # Create the fs output directory if necessary if not os.path.isdir(fsdir): os.makedirs(fsdir) # Call freesurfer cmd = ["recon-all", "-all", "-subjid", sid, "-i", anatfile, "-sd", fsdir] recon = FSWrapper(cmd, shfile=fsconfig) recon() if recon.exitcode != 0: raise FreeSurferRuntimeError(recon.cmd[0], " ".join(recon.cmd[1:]), recon.stderr + recon.stdout) subjfsdir = os.path.join(fsdir, sid) return subjfsdir