Introduction

Some times, you want to plot the 3d-surfaces from FreeSurfer. Here, it is easier to use data from FreeSurfer (like annot and colorlut files) to create colours for the vectors. The data is somewhat more complex than the 2d ggplot polygon version ggseg ggseg3d() will create a plotly plot, which is interactive, and provides another type of flexibility to the user. A lot of credit goes to A.M.Winkler and his Brainder work, which supplied us with the first examples of going from .srf to .ply files, and whose scripts massively aided us in making this work.

Basic use

The function ggseg3d(), is based in the plotly, it is recommended to get a little familiarized with with plotly.

Out-of-the-box, ggseg() works without supplying any extra information. It will create a base plot of the aparc (dk) brain segmentations. All [...]_3d atlases have a built in colour column for default colour plotting of the segments.

ggseg3d() %>% 
  remove_axes() %>% 
  pan_camera("right medial")

Atlas data

The data is stored in tibbles, and looks like so:

dk_3d
## # A tibble: 4 x 4
##   atlas surf     hemi  ggseg_3d         
##   <chr> <chr>    <chr> <list>           
## 1 dk_3d inflated right <tibble [36 × 6]>
## 2 dk_3d inflated left  <tibble [36 × 6]>
## 3 dk_3d LCBC     right <tibble [36 × 6]>
## 4 dk_3d LCBC     left  <tibble [36 × 6]>

To grab all the data for a surface and hemisphere, you should reduce the data to one line, and then unnest()

dk_3d %>% 
  filter(surf == "inflated" & hemi == "right") %>% 
  unnest(cols = ggseg_3d)
## # A tibble: 36 x 9
##    atlas surf   hemi  region       colour  mesh     label       roi   annot     
##    <chr> <chr>  <chr> <chr>        <chr>   <list>   <chr>       <chr> <chr>     
##  1 dk_3d infla… right <NA>         <NA>    <named … rh_unknown  0001  unknown   
##  2 dk_3d infla… right bankssts     #196428 <named … rh_bankssts 0002  bankssts  
##  3 dk_3d infla… right caudal ante… #7D64A0 <named … rh_caudala… 0003  caudalant…
##  4 dk_3d infla… right caudal midd… #641900 <named … rh_caudalm… 0004  caudalmid…
##  5 dk_3d infla… right corpus call… #784632 <named … rh_corpusc… 0005  corpuscal…
##  6 dk_3d infla… right cuneus       #DC1464 <named … rh_cuneus   0006  cuneus    
##  7 dk_3d infla… right entorhinal   #DC140A <named … rh_entorhi… 0007  entorhinal
##  8 dk_3d infla… right fusiform     #B4DC8C <named … rh_fusiform 0008  fusiform  
##  9 dk_3d infla… right inferior pa… #DC3CDC <named … rh_inferio… 0009  inferiorp…
## 10 dk_3d infla… right inferior te… #B42878 <named … rh_inferio… 0010  inferiort…
## # … with 26 more rows

External data supply

Particularly notice the mesh column, which is a list column of lists. In there is all the 6 vectors needed to create the mesh of the tri-surface plot. You’ll also need to notice the label, annot and region columns, which are likely the columns you will be matching on when providing with your own data for colours. You need to be meticulous when fixing your data, be sure it matches. The function should give you a warning if it’s struggling to match something.

The column you want to use for colour, needs to be supplied to the colour option, and you’ll likely want to supply it to the text option, as this will add another line to the plotly hover information.

someData = dk_3d %>% 
  filter(surf == "inflated" & hemi == "right") %>% 
  unnest(ggseg_3d) %>% 
  ungroup() %>% 
  select(region) %>% 
  na.omit() %>% 
  mutate(p = sample(seq(0,.5, length.out = 100 ), nrow(.)) %>% 
           round(2)) 

ggseg3d(.data = someData, 
        atlas = dk_3d,
        colour = "p", text = "p") %>% 
  pan_camera("right medial")

Colours

You can provide custom colour palettes either in hex or R-names

ggseg3d(.data = someData, atlas = dk_3d, 
        colour = "p", text = "p", 
        palette = c("forestgreen", "white", "firebrick"))

A new improvement now allows you to also supply a named vector as palette, to control the breakpoints of the palette values, and allow you to have a colour bar that exceeds the values that exist in the data plotted.

ggseg3d(.data = someData, atlas = dk_3d, 
        colour = "p", text = "p", 
        palette = c("forestgreen" = 0, "white" = .05, "firebrick" = 1))

If you are plotting the sub-cortical structures, you might want to reduce the opacity of the NA structures, so that you can see the more medial structures. you may also want to add the glassbrain.

somData_aseg = aseg_3d %>% 
  unnest(cols = ggseg_3d) %>% 
  select(label) %>% 
  filter(!grepl("Ventricle|Putamen|Amygdala", label)) %>% 
  mutate(p = seq(1, nrow(.))) 

ggseg3d(.data = somData_aseg, atlas = aseg_3d, 
        colour = "p", text = "p", 
        na.alpha= .5) %>% 
  add_glassbrain()

Legend options

A new feature now also allows you to control the colourbar appearance through the colourbar option in plotly::add_trace, through the legend.option argument. This argument takes a list, and you can for instance change the title.

 ggseg3d(.data=someData,
      colour = "p", text="p", 
      options.legend = list(title=list(text="mm")))

Camera

You can rotate away all you like. if you want to start from the medial view, you can use the pan_camera option

ggseg3d(.data = someData, atlas=dk_3d, colour = "p") %>% 
  pan_camera("right medial")

There is much more you can do with the camera options, or by adding extra layout options to plotly. You may want to have a look at these resources if you want to do more complex adaptations to the plots:

https://plot.ly/r/axes/#modifying-axes-for-3d-plots
https://plot.ly/r/trisurf/
https://moderndata.plot.ly/trisurf-plots-in-r-using-plotly/