This R script parses manual astrocyte, microglia, plaque, and tangle annotations created using the VGG Image Annotator tool into an ImageJ-readable format.
Load requisite packages and define directories. Note that this script uses my personal utilities package brainstorm
, which can be downloaded via devtools::install_github("ayushnoori/brainstorm")
.
# data manipulation
library(data.table)
library(purrr)
library(magrittr)
# string manipulation
library(stringi)
# utility functions
library(brainstorm)
Note that directories are relative to the R project path.
Read mappings between true crop labels and random alphanumeric IDs generated by prior ImageJ script.
celltypes = c("Astrocyte", "Microglia", "Plaque", "Tangle")
# read astrocyte, microglia, and plaque mappings
read_map = function(celltype) { return(fread(file.path(ddir, celltype, "ID Mappings.csv"))[, Type := celltype]) }
IDmap = rbindlist(map(celltypes, ~read_map(.x)))[, V1 := NULL]
setnames(IDmap, "Type", "Group")
show_table(head(IDmap, 20))
Read VGG Image Annotator (VIA) annotations from the output .csv
files.
flist = list.files(path = dir2.1, pattern = "\\.csv$")
# function to read annotations
read_annot = function(file) {
fread(file.path(dir2.1, file)) %>%
.[, c("Annotator", "Group") := as.list(stri_split_fixed(file, "_", simplify = T)[1:2])] %>%
return()
}
# read annotations
annot = rbindlist(map(flist, ~read_annot(.x)))
# remove empty file labels and .png extension suffix
annot = annot %>%
.[region_shape_attributes != "{}", ] %>%
.[, Filename := gsub(".png", "", filename)] %>%
.[, Annotator := factor(Annotator, levels = c("alberto", "ayush", "clara"), labels = c("ASP", "AN", "CMC"))] %>%
.[, Group := factor(Group, levels = c("astrocyte", "microglia", "plaque", "tangle"), labels = celltypes)]
show_table(annot[sample(nrow(annot), 40), ])
Define function to parse VIA annotations into format readable by ImageJ. To conform with ImageJ naming conventions, the cell-type of each ROI is specified by Group
, while the subtype (e.g., compact
vs. diffuse
or soma
vs. process
) is specified by type
.
# define function to parse label based on VIA output
parse_label = function(label, grp = c("region", "region_shape")) {
label = label %>%
strsplit(., ",", fixed = TRUE) %>% .[[1]] %>% # split by comma to separate vars
gsub("[{, }, \"]", "", .) %>% # remove brackets and quotes
strsplit(., ":")
# extract second elements in list, then assign first elements as names
parsed = map(label, 2)
names(parsed) = map_chr(label, 1)
# replace tangle annotation name
if("tangle" %in% names(parsed)) { names(parsed) = "type"; parsed[["quality"]] = "none" }
# return statement
if(grp == "region") return(parsed[c("type", "quality")]) else return(parsed[c("x", "y", "width", "height")])
}
Apply parse_label
function to VIA annotations, and join with IDmap
.
# parse annotations
annot = annot %>%
.[, c("Type", "Quality") := map_dfr(region_attributes, ~parse_label(.x, "region"))] %>%
.[, c("X", "Y", "Width", "Height") := map_dfr(region_shape_attributes, ~parse_label(.x, "region_shape"))] %>%
.[Type == "vessel", Group := "Vessel"] %>%
.[, filename := gsub(".png", "", filename)]
# join with ID mapping information, consolidate microglia and plaque categories, order table
IDmap[, Group := NULL]
dat = merge(IDmap, annot, by.x = "ID", by.y = "filename", all.y = TRUE) %>%
.[, .(Sample, Layer, Crop, Condition, File, Group, X, Y, Width, Height, Type, Quality, Annotator)] %>%
.[order(Sample, Layer, Crop, Group, as.numeric(X)), ]
# total ROI count
summary(factor(dat[, Group]))
show_table(dat[sample(nrow(dat), 40), ])
Write VIA ROIs to appropriate output.
# remove prior output
rmlist = list.files(path = dir2, pattern = "\\.csv$")
for (rm in rmlist){ file.remove(file.path(dir2, rm)) }
# write file list
files = unique(dat$File)
cat(paste0("Annotated Crops: ", length(files), "\n"))
write(c("Annotated TIFFs", files), file.path(dir2, "Annotated TIFFs.txt"))
# save VIA annotations of each crop after ordering by Type, then X coord.
walk(files, ~fwrite(dat[File == .x, ], file.path(dir2, paste0(.x, ".csv"))))
If you see mistakes or want to suggest changes, please create an issue on the source repository.