knitr::opts_chunk$set(echo = TRUE)
rgl::setupKnitr()

Example data

First we specify a subjects dir and the subject we want to use. Feel free to replace this with your data.

library('fsbrain')
sjd = '~/data/subject1_only'
sj = 'subject1'

Now increase the figure size.

fsbrain.set.default.figsize(1000, 1000);

Visualizing native space morphometry data: cortical thickness

Let's start with a basic example and plot cortical thickness on the surface of our subject:

vis.subject.morph.native(sjd, sj, 'thickness')

This loaded the morphometry data files surf/lh.thickness and surf/rh.thickness of the subject, applied a colormap to the values, and visualized the results on the white surface, i.e., the brain surface meshes in the files surf/lh.white and surf/rh.white.

Changing the view and masking the medial wall

The default view above shows the brain from four different angles in four tiles and is called t4 view. Maybe you prefer a different view? In addition, let's mask the medial wall (the large blue parts in the lower two tiles).

vis.subject.morph.native(sjd, sj, 'thickness', views='t9', cortex_only = TRUE)

The mask for the medial wall was created by setting the data of all vertices which are not part of the cortex to NA, based on the cortex definition in the label files label/lh.cortex.label and label/rh.cortex.label of the subject.

Visualizing data with outliers, tails, or other non-normal data

Next, we will try a third view, add a colorbar, use the pial surface, and plot something different. Let's try mean curvature.

vis.subject.morph.native(sjd, sj, 'curv', views='si', cortex_only = TRUE, draw_colorbar = 'vertical', surface = 'pial')

That does not look very exciting. Why? The curvature data has long tails and is far from Normal:

hist(subject.morph.native(sjd, sj, 'curv', hemi = 'both'))

There are various solutions to this problem, but for plotting, the easiest one is to apply some function that removes outliers or performs some other sort of data transformation before plotting. You can pass any transformation function you want using rglactions, in the next example we clip the data.

The si view is single interactive view, and you can rotate the brain with your mouse in an R session. That is not possible in this markdown document though, so better try it in R. We will use another view here and switch back to the white surface.

vis.subject.morph.native(sjd, sj, 'curv', views='sd_medial_lh', cortex_only = TRUE, draw_colorbar = 'horizontal',  rglactions=list('trans_fun'=clip.data))

That looks more suitable.

Standard space data

In group studies, you typically have a single standard space that data gets mapped to for comparison. For FreeSurfer surface data, typically the fsaverage subject is used, but you could create and use your own template, of course. Let's have a look at some standard space data mapped to fsaverage. We will use sulcal depths in this example.

vis.subject.morph.standard(sjd, sj, 'sulc', cortex_only = TRUE, fwhm="5");

This loaded the morphometry data files surf/?h.curv.fwhm5.fsaverage.mgh of the subject, applied a colormap to the values, and visualized the results on the white surface of fsaverage (also using the fsaverage cortex labels). The function will do its best to find fsaverage automatically, but if it fails or you want to use a different template subject, have a look at the optional parameters template_subject and template_subjects_dir.

When data is mapped to standard space, they will get smoothed and different versions with a number of kernel settings will be available after running FreeSurfer's recon-all with the qcache option. That is what the fwhm parameter is about. Try different values depending on what files you have, start with "5", "10", or "15".

Adjusting the colormap

The exact colormap you use is a matter of taste, but you should maybe chose a type of colormap that is suitable for your data: sequential like heat for morphometry data, diverging like cm.colors for positive and negative clusters, or qualitative like Set3 from the RColorbrewer package for atlas regions. The default colormap used by fsbrain is a sequential multi-hue palette (squash::rainbow2).

A great and popular sequential palette is viridis from the viridis package. Here we used the makecmap_options parameter to use it:

vis.subject.morph.standard(sjd, sj, 'sulc', cortex_only = TRUE, fwhm="15", makecmap_options = list('colFn'=viridis::viridis), draw_colorbar = T, views='sd_dorsal');

Try some different colormaps and see which one you like best. If you want to use interpolated colormaps with many colors based on the RColorBrewer palettes (which have too few colors for morphometry data in most cases), try something like this:

manyblues = colorRampPalette(RColorBrewer::brewer.pal(9, name="Blues"));
vis.subject.morph.standard(sjd, sj, 'area', makecmap_options = list('colFn'=manyblues), draw_colorbar = T, views='sd_dorsal');

If you prefer a smoother colorbar, try increasing the number of colors by adapting the makecmap_options parameter, e.g.:

makecmap_options = list('colFn'=manyblues, 'n=100')

Plotting atlas data

Plotting a brain parcellation is straightforward, here we plot the Desikan atlas:

vis.subject.annot(sjd ,sj, 'aparc');

This loads the Desikan labels for the subject from the files label/?h.aparc.annot and uses colors from the contained color lookup table.

By default, FreeSurfer computes brain parcellations for the following brain atlases:

Of course, one can select different view angles, surfaces, and other options as illustrated above for morphometry data.

Annotation Outlines

A unique option that is available with annotations is to draw outlines around them:

vis.subject.annot(sjd ,sj, 'aparc', outline = TRUE, surface="inflated");

You can customize the look of the outlines (or region borders) by passing a list of extra arguments to annot.outline instead of a logical value, see the documentation for details. Here is a more complex example that draws a green border around the middle temporal gyrus and a blue border around the postcentral gyrus:

vis.subject.annot(sjd ,sj, 'aparc', outline=list('outline_color'=c('green', 'blue'), 'limit_to_regions'=c('middletemporal', 'postcentral'), expand_inwards=2L), surface="inflated", views="sd_lateral_lh");

Note that you can also use this function for drawing borders around arbitrary custom regions (activation cluster or whatever). A custom region really is nothing but a label, and you can convert a label (or several labels) to an annotation using label.to.annot.

Labels

Here is an example for visualizing a label:

vis.subject.label(sjd, sj, 'cortex', hemi='lh', views = 'sd_medial_lh');

Here, the file label/lh.cortex.label was used. If you want to visualize preloaded data, there is no need to save it to a file first, just use the vis.labeldata.on.subject function.

Results: region-based

If you perform atlas based analyses, you will typically get one result (significance, effect size, or whatever) per atlas region. The function vis.region.values.on.subject comes in very handy here. Typically the results are displayed on the fsaverage subject, but you can use any subject you want:

atlas = 'aparc';   # Desikan atlas
# For the left hemisphere, we just assign a subset of the
# atlas regions. The others will get the default value.
lh_region_value_list = list("bankssts"=0.9, "precuneus"=0.7, "postcentral"=0.8, "lingual"=0.6);
# For the right hemisphere, we retrieve the full list of regions for
# the atlas, and assign random values to all of them.
atlas_region_names = get.atlas.region.names(atlas, template_subjects_dir = sjd, template_subject = sj);
rh_region_value_list = rnorm(length(atlas_region_names), 0.8, 0.2);
names(rh_region_value_list) = atlas_region_names;

vis.region.values.on.subject(sjd, sj, atlas, lh_region_value_list, rh_region_value_list, draw_colorbar = T);

If your data are symmetric around zero, e.g., t-values, you should use a symmetric colormap. An example is available in the default makecmap_topions settings of the vis.symmetric.data.on.subject function, which we will use next.

Results: vertex-based

When performing vertex-wise analyses in standard space, you get one result per vertex, e.g., a t-map, which may be thresholded to yield clusters. Here is an example that visualizes the clusters and uses a gray-scale background computed from the mean curvature of the fsaverage cortex:

subjects_dir = file.path(find.freesurferhome()$found_at, 'subjects');
subject_id = 'fsaverage';

lh_demo_cluster_file = system.file("extdata", "lh.clusters_fsaverage.mgz", package = "fsbrain", mustWork = TRUE);
rh_demo_cluster_file = system.file("extdata", "rh.clusters_fsaverage.mgz", package = "fsbrain", mustWork = TRUE);

lh_clust = freesurferformats::read.fs.morph(lh_demo_cluster_file);   # contains a single positive cluster (activation, group difference), the other values are 0
rh_clust = freesurferformats::read.fs.morph(rh_demo_cluster_file);   # contains two negative clusters

vis.symmetric.data.on.subject(subjects_dir, subject_id, lh_clust, rh_clust, bg="curv_light");

Learning more

To learn more, read the vignettes:

browseVignettes('fsbrain');

In general, the vignettes are a great way to get started with fsbrain. You can also read them online at CRAN, they are linked on the fsbrain page on GitHub.



dfsp-spirit/fsbrain documentation built on Nov. 28, 2024, 10:29 a.m.