Skip to content

Commit

Permalink
add_annotation automatically compiles annotations from template if …
Browse files Browse the repository at this point in the history
…missing
  • Loading branch information
dipterix committed Jan 22, 2025
1 parent dd4b0b0 commit af5b9c9
Show file tree
Hide file tree
Showing 8 changed files with 332 additions and 5 deletions.
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## Changes since last CRAN release
* `27324b88 (HEAD -> master)` [_`dipterix`_]: Fixing the side panels so the background color is always black
* `ad4633fe (origin/master, origin/HEAD)` [_`dipterix`_]: Canvas and screenshot is transparent when background is `#ffffff`
* `dd4b0b0a (HEAD -> master, origin/master, origin/HEAD)` [_`dipterix`_]: Fixing the side panels so the background color is always black
* `ad4633fe` [_`dipterix`_]: Canvas and screenshot is transparent when background is `#ffffff`
* `ff5eca46` [_`dipterix`_]: Fixed shader ignoring always-depth flag when outline is off; updated `jsPDF` to the 2.5.2 build
* `ad202235` [_`dipterix`_]: Transition animation allows video recording
* `94ec004f` [_`dipterix`_]: Fixed screenshot button and improved video quality
Expand Down
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: threeBrain
Type: Package
Title: Your Advanced 3D Brain Visualization
Version: 1.2.0.9009
Version: 1.2.0.9010
Authors@R: c(
person("Zhengjia", "Wang", email = "[email protected]", role = c("aut", "cre", "cph")),
person("John", "Magnotti", email = "[email protected]", role = c("ctb", "res")),
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export(freeserfer_colormap)
export(freesurfer_brain)
export(freesurfer_brain2)
export(freesurfer_lut)
export(generate_cortical_parcellation)
export(generate_smooth_envelope)
export(generate_subcortical_surface)
export(geom_freemesh)
Expand Down
23 changes: 21 additions & 2 deletions R/class_brain.R
Original file line number Diff line number Diff line change
Expand Up @@ -271,12 +271,15 @@ Brain2 <- R6::R6Class(

},

add_annotation = function(annotation, surface_type = "pial") {
add_annotation = function(annotation, surface_type = "pial", template_subject = "fsaverage") {
if(tolower(surface_type) == "pial.t1") {
surface_type <- "pial"
}
if(!surface_type %in% self$surface_types) {
return(invisible())
self$add_surface(surface_type)
if(!surface_type %in% self$surface_types) {
return(invisible())
}
}
annot_dir <- dirname(annotation)
annot_fname <- filename(annotation)
Expand All @@ -294,6 +297,22 @@ Brain2 <- R6::R6Class(

surface_instance <- self$surfaces[[surface_type]]

if(!length(lh_path) || !length(rh_path)) {
# annot file is missing; generate one on the fly
tryCatch({
generate_cortical_parcellation(brain = self,
template_subject = template_subject,
annotation = annot_fname,
add_annotation = FALSE)
lh_path <- file.path(label_path, sprintf(c("lh.%s.annot", "lh.%s"), annot_fname))
lh_path <- lh_path[file.exists(lh_path)]
rh_path <- file.path(label_path, sprintf(c("rh.%s.annot", "rh.%s"), annot_fname))
rh_path <- rh_path[file.exists(rh_path)]
}, error = function(e) {
warning(e)
})
}

if(length(lh_path)) {
surface_instance$left_hemisphere$set_annotation(annotation, lh_path[[1]])
}
Expand Down
2 changes: 2 additions & 0 deletions R/class_brainatlas.R
Original file line number Diff line number Diff line change
Expand Up @@ -248,3 +248,5 @@ create_voxel_cube <- function(mni_ras, value, colormap,
}
re
}


147 changes: 147 additions & 0 deletions R/template_cortical-annotation.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
#' Generate cortical annotations from template using surface mapping
#' @description
#' This is a low-level function. Use \code{brain$add_annotation} instead.
#'
#' @param brain Brain object
#' @param template_subject template subject where the annotation is stored
#' @param annotation annotation name in the label folder; default is
#' \code{'Yeo2011_7Networks_N1000'}, standing for
#' \code{'lh.Yeo2011_7Networks_N1000.annot'} and
#' \code{'rh.Yeo2011_7Networks_N1000.annot'}.
#' @param add_annotation whether to add annotation to \code{brain}
#' @returns \code{brain} with the annotation added if \code{add_annotation}
#' is true
#'
#' @export
generate_cortical_parcellation <- function(
brain, template_subject = "fsaverage", annotation = "Yeo2011_7Networks_N1000",
add_annotation = TRUE) {

# DIPSAUS DEBUG START
# devtools::load_all()
# brain <- raveio::rave_brain("demo/DemoSubject")
# template_subject <- "fsaverage"
# annotation <- "Yeo2011_17Networks_N1000"

if(is.null(brain$surfaces$sphere.reg)) {
brain$add_surface("sphere.reg")
on.exit({
brain$surfaces$sphere.reg <- NULL
})
}

if(!length(brain$surfaces$sphere.reg)) {
stop("The FreeSurfer brain object does not have `sphere.reg` surface. Did you finish `recon-all`?")
}
# make sure fsaverage is present
tempolate_path <- file.path(default_template_directory(), template_subject)
if(!dir.exists(tempolate_path)) {
message("Template `", template_subject, "` is missing. Downloading it from RAVE (this is one-time procedure).")
message(sprintf("Running `threeBrain::download_template_subject(subject_code = '%s')`", template_subject))
download_template_subject(subject_code = template_subject)
}

# check if the annoations are available
lh_annot_path <- file.path(tempolate_path, "label", sprintf("lh.%s.annot", annotation))
rh_annot_path <- file.path(tempolate_path, "label", sprintf("rh.%s.annot", annotation))

if(!file.exists(lh_annot_path)) {
stop(sprintf("Template `%s` does not have FreeSurfer annotation `%s` for the left hemisphere (%s is missing).", template_subject, annotation, basename(lh_annot_path)))
}
if(!file.exists(rh_annot_path)) {
stop(sprintf("Template `%s` does not have FreeSurfer annotation `%s` for the right hemisphere (%s is missing).", template_subject, annotation, basename(rh_annot_path)))
}

# sphere.reg for template
lh_sphere_reg_path_template <- file.path(tempolate_path, "surf", "lh.sphere.reg")
rh_sphere_reg_path_template <- file.path(tempolate_path, "surf", "rh.sphere.reg")

# read sphere.reg for native
lh_sphere_reg_path <- file.path(brain$base_path, "surf", "lh.sphere.reg")
rh_sphere_reg_path <- file.path(brain$base_path, "surf", "rh.sphere.reg")

# build mapping for each native node

# left
sphere_reg <- freesurferformats::read.fs.surface(lh_sphere_reg_path)
sphere_reg_template <- freesurferformats::read.fs.surface(lh_sphere_reg_path_template)
annot_template <- freesurferformats::read.fs.annot(lh_annot_path)

kdtree <- ravetools::vcg_kdtree_nearest(
target = sphere_reg_template$vertices[, 1:3, drop = FALSE],
query = sphere_reg$vertices[, 1:3, drop = FALSE],
k = 1
)
new_label_codes <- annot_template$label_codes[kdtree$index[, 1]]

annot_path <- file.path(brain$base_path, "label", sprintf("lh.%s.annot", annotation))
freesurferformats::write.fs.annot(
filepath = annot_path, num_vertices = as.integer(length(new_label_codes)),
colortable = annot_template$colortable_df, labels_as_colorcodes = new_label_codes)


# pial_annotated <- merge(
# ieegio::read_surface(file.path(brain$base_path, "surf", "lh.pial")),
# ieegio::read_surface(annot_path)
# ); plot(pial_annotated)

# right
sphere_reg <- freesurferformats::read.fs.surface(rh_sphere_reg_path)
sphere_reg_template <- freesurferformats::read.fs.surface(rh_sphere_reg_path_template)
annot_template <- freesurferformats::read.fs.annot(rh_annot_path)

kdtree <- ravetools::vcg_kdtree_nearest(
target = sphere_reg_template$vertices[, 1:3, drop = FALSE],
query = sphere_reg$vertices[, 1:3, drop = FALSE],
k = 1
)
new_label_codes <- annot_template$label_codes[kdtree$index[, 1]]

annot_path <- file.path(brain$base_path, "label", sprintf("rh.%s.annot", annotation))
freesurferformats::write.fs.annot(
filepath = annot_path, num_vertices = as.integer(length(new_label_codes)),
colortable = annot_template$colortable_df, labels_as_colorcodes = new_label_codes)

# pial_annotated <- merge(
# ieegio::read_surface(file.path(brain$base_path, "surf", "rh.pial")),
# ieegio::read_surface(annot_path)
# ); plot(pial_annotated)


# # right
# rh_sphere_reg <- ieegio::read_surface(rh_sphere_reg_path)
# rh_sphere_reg_template <- ieegio::read_surface(rh_sphere_reg_path_template)
# rh_annot_template <- ieegio::read_surface(rh_annot_path)
#
# kdtree <- ravetools::vcg_kdtree_nearest(
# t(rh_sphere_reg_template$geometry$vertices[1:3, , drop = FALSE]),
# t(rh_sphere_reg$geometry$vertices[1:3, , drop = FALSE]),
# 1
# )
# mapping_index <- as.vector(kdtree$index)
#
# # mapping_index <- apply(rh_sphere_reg$geometry$vertices, 2, function(p) {
# # which.min(colSums((rh_sphere_reg_template$geometry$vertices - p)^2))
# # })
# annot_name <- names(rh_annot_template$annotations$data_table)[[1]]
# new_annot <- rh_annot_template$annotations$data_table[[1]][mapping_index]
#
# rh_annot <- rh_annot_template
# rh_annot$sparse_node_index <- structure(seq_along(new_annot), start_index = 1L)
# annot_table <- structure(list(as.integer(new_annot)), names = annot_name)
# rh_annot$annotations$data_table <- data.table::as.data.table(annot_table)
#
# pial_annotated <- merge(
# ieegio::read_surface(file.path(brain$base_path, "surf", "rh.pial")),
# rh_annot)
# plot(pial_annotated, method = "r3js")

# # save
# ieegio::write_surface(x = rh_annot, con = file.path(brain$base_path, "label", sprintf("rh.%s.annot", annotation)), format = "freesurfer", type = "annotations")

if( add_annotation ) {
brain$add_annotation(sprintf("label/%s", annotation))
}
invisible(brain)

}
126 changes: 126 additions & 0 deletions adhoc/cortical_mapping.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@



generate_cortical_parcellation <- function(
brain, template_subject = "fsaverage", annotation = "Yeo2011_7Networks_N1000") {

# DIPSAUS DEBUG START
# devtools::load_all()
# brain <- raveio::rave_brain("demo/DemoSubject")
# template_subject <- "fsaverage"
# annotation <- "Yeo2011_17Networks_N1000"

if(is.null(brain$surfaces$sphere.reg)) {
brain$add_surface("sphere.reg")
on.exit({
brain$surfaces$sphere.reg <- NULL
})
}

if(!length(brain$surfaces$sphere.reg)) {
stop("The FreeSurfer brain object does not have `sphere.reg` surface. Did you finish `recon-all`?")
}
# make sure fsaverage is present
tempolate_path <- file.path(default_template_directory(), template_subject)
if(!dir.exists(tempolate_path)) {
message("Template `", template_subject, "` is missing. Downloading it from RAVE (this is one-time procedure).")
message(sprintf("Running `threeBrain::download_template_subject(subject_code = '%s')`", template_subject))
download_template_subject(subject_code = template_subject)
}

# check if the annoations are available
lh_annot_path <- file.path(tempolate_path, "label", sprintf("lh.%s.annot", annotation))
rh_annot_path <- file.path(tempolate_path, "label", sprintf("rh.%s.annot", annotation))

if(!file.exists(lh_annot_path)) {
stop(sprintf("Template `%s` does not have FreeSurfer annotation `%s` for the left hemisphere (%s is missing).", template_subject, annotation, basename(lh_annot_path)))
}
if(!file.exists(rh_annot_path)) {
stop(sprintf("Template `%s` does not have FreeSurfer annotation `%s` for the right hemisphere (%s is missing).", template_subject, annotation, basename(rh_annot_path)))
}

# sphere.reg for template
lh_sphere_reg_path_template <- file.path(tempolate_path, "surf", "lh.sphere.reg")
rh_sphere_reg_path_template <- file.path(tempolate_path, "surf", "rh.sphere.reg")

# read sphere.reg for native
lh_sphere_reg_path <- file.path(brain$base_path, "surf", "lh.sphere.reg")
rh_sphere_reg_path <- file.path(brain$base_path, "surf", "rh.sphere.reg")

# build mapping for each native node

# left
sphere_reg <- freesurferformats::read.fs.surface(lh_sphere_reg_path)
sphere_reg_template <- freesurferformats::read.fs.surface(lh_sphere_reg_path_template)
annot_template <- freesurferformats::read.fs.annot(lh_annot_path)

kdtree <- ravetools::vcg_kdtree_nearest(
target = sphere_reg_template$vertices[, 1:3, drop = FALSE],
query = sphere_reg$vertices[, 1:3, drop = FALSE],
k = 1
)
new_label_codes <- annot_template$label_codes[kdtree$index[, 1]]

annot_path <- file.path(brain$base_path, "label", sprintf("lh.%s.annot", annotation))
freesurferformats::write.fs.annot(
filepath = annot_path, num_vertices = as.integer(length(new_label_codes)),
colortable = annot_template$colortable_df, labels_as_colorcodes = new_label_codes)


# pial_annotated <- merge(
# ieegio::read_surface(file.path(brain$base_path, "surf", "lh.pial")),
# ieegio::read_surface(annot_path)
# ); plot(pial_annotated)

# right
sphere_reg <- freesurferformats::read.fs.surface(rh_sphere_reg_path)
sphere_reg_template <- freesurferformats::read.fs.surface(rh_sphere_reg_path_template)
annot_template <- freesurferformats::read.fs.annot(rh_annot_path)

kdtree <- ravetools::vcg_kdtree_nearest(
target = sphere_reg_template$vertices[, 1:3, drop = FALSE],
query = sphere_reg$vertices[, 1:3, drop = FALSE],
k = 1
)
new_label_codes <- annot_template$label_codes[kdtree$index[, 1]]

annot_path <- file.path(brain$base_path, "label", sprintf("rh.%s.annot", annotation))
freesurferformats::write.fs.annot(
filepath = annot_path, num_vertices = as.integer(length(new_label_codes)),
colortable = annot_template$colortable_df, labels_as_colorcodes = new_label_codes)

# pial_annotated <- merge(
# ieegio::read_surface(file.path(brain$base_path, "surf", "rh.pial")),
# ieegio::read_surface(annot_path)
# ); plot(pial_annotated)

#
# # right
# rh_sphere_reg <- ieegio::read_surface(rh_sphere_reg_path)
# rh_sphere_reg_template <- ieegio::read_surface(rh_sphere_reg_path_template)
# rh_annot_template <- ieegio::read_surface(rh_annot_path)
#
# kdtree <- ravetools::vcg_kdtree_nearest(
# t(rh_sphere_reg_template$geometry$vertices[1:3, , drop = FALSE]),
# t(rh_sphere_reg$geometry$vertices[1:3, , drop = FALSE]),
# 1
# )
# mapping_index <- as.vector(kdtree$index)
# annot_name <- names(rh_annot_template$annotations$data_table)[[1]]
# new_annot <- rh_annot_template$annotations$data_table[[1]][mapping_index]
#
# rh_annot <- rh_annot_template
# rh_annot$sparse_node_index <- structure(seq_along(new_annot), start_index = 1L)
# annot_table <- structure(list(as.integer(new_annot)), names = annot_name)
# rh_annot$annotations$data_table <- data.table::as.data.table(annot_table)
#
# # pial_annotated <- merge(ieegio::read_surface(file.path(brain$base_path, "surf", "rh.pial")), rh_annot)
# # plot(pial_annotated, method = "r3js")
#
# # save
# ieegio::write_surface(x = rh_annot, con = file.path(brain$base_path, "label", sprintf("rh.%s.annot", annotation)), format = "freesurfer", type = "annotations")

brain$add_annotation(sprintf("label/%s", annotation))
invisible(annotation)

}
32 changes: 32 additions & 0 deletions man/generate_cortical_parcellation.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit af5b9c9

Please sign in to comment.