This software is primarily meant to be used for classification of images of cell-based assays for neuronal surface autoantibody detection or similar techniques, which is the standard diagnostic and research method of use in the field of autoantibody-driven autoimmune encephalitis, see e.g. “Detection Methods for Autoantibodies in Suspected Autoimmune Encephalitis”, by Ricken et al, Frontiers in Neurology, 2018. Even as images of cell-based assays for a long time have been commonplace in the diagnostic procedure, the most wide-spread way of grading these is still by expert inspection and opinion. This is in my opinion problematic both as it hinders the dissemiation of research to environments that lack the sample volumes that can enable the training of specialists in the area, and furthermore it might increase the inter-rater variance. Thirdly, to be able to screen through thousands of samples, which is necessary to reach more epidemiological conclusions in this field, an automated screening methodology is warranted. This package has been created to address these needs. It takes images of cell-based assays as input and creates a composite score from these, that then can be used to classify samples as negative or positive for a certain antibody-specificity. The reason for the name “islify” is that I during its creation have thought about the individual picture as an archielago where we with different filters control the water level as well as ground characteristica, thereby creating and describing islands of interest.
Below, the standard workflow of islify is presented, with two different user cases; situations with and without a reference color. A reference color in this context refers to a color that is co-localized with the color of interest (here called “focus”). The most common example in antibody-screening situations would be that the focus protein is fused to EGFP. In such cases, it is only in areas where EGFP is expressed that any specific antibody-binding to the focus protein is expected. In both cases, a nuclear staining is suggested, even if not mandatory, to simplify the definition of the nuclear diameter - an important compontent to the filters of the islify function. The islify function is the central component this software. It generates a score per image that corresponds to the fraction of the total number of pixels that are taken up by “islands”, i.e. confluences of high-intensity signal in the focus channel.
This is how to install the package, if that has not already been done:
if (!requireNamespace("BiocManager", quietly = TRUE)) {
install.packages("BiocManager")
}
BiocManager::install("islify")
The example data provided consists of small areas of two images, one with positive antibody staining and one lacking such signal. The focus protein in question is fused to EGFP. Furthermore, their nuclei are stained with DAPI.
library(islify)
data(posImage)
data(negImage)
This function provides an excellent first step to get an overview of the internals of the image files.
quantIntensities <- getQuantileIntensities(list(posImage),
quantiles = c(0.9, 0.95, 0.99)
)
quantIntensities
## [[1]]
## Percent_90 Percent_95 Percent_99
## 1 0.12813333 0.18867333 0.3681333
## 2 0.04556667 0.07580167 0.2476340
## 3 0.25000000 0.29820000 0.3794010
This output tells us a number of things:
With saveImage, you can save any one of the colors in the data, as well as a merge of all colors. The standard user case is including all individual colors as well as the merge:
saveImage(list(posImage, negImage), c("pos", "neg"),
frameNums = "All",
frameCols = c("R", "G", "B"), truncTo =
quantIntensities[[1]][, "Percent_95"],
outDir = "."
)
This generates eight png files in the outDir directory, divided in
per color sub-directories. These eight images are summarised here:
These images shows us a number of things, including that with this specific
HEK-derived cell-line, the nuclei are considerably smaller than the cells in
many instances, and further that areas with very high EGFP expression does not
always correlate strongly to high IgG binding. These plots also tell us that
it seems like we got hte colors right: the blue color does look like the
expected nuclear staining, and the green is as prevalent in the negImage as
the posImage, whereas the red image is negative for the negImage, which makes
sense, given that both were transfected, but the negImage was stained with
negative control serum.
Now, we go into the pre-islify phase, where the nuclear size threshold as well as the intensity threshold(s) are defined.
Here, the nuclear size is defined. In the algorithm, the nuclear size is used to define the narrowest allowed part of an island. As we are using the nuclear staining, and we know that the blue DAPI staining is in the third frame of the image objects in this case, we set frameNum to 3.
sizeCutoff <- getSizeCutoff(imgDirs = list(negImage, posImage), frameNum = 3)
sizeCutoff
## [1] 39.5
Now, we are going to define the background noise level for the focus channel, (here the red IgG) and in applicable cases, such as with the example data, repeat the procedure for the reference chanel, in this case the EGFP. We start with the IgG/focus channel:
intensityCutoffRed <- getIntensityCutoff(
imgDirs = list(negImage, posImage),
frameNum = 1,
ignore_white =
quantIntensities[[1]][, "Percent_90"][1]
)
intensityCutoffRed
## [1] 0.025
There is no perfect way of doing this: with the selected standard method, Triangle, we seem to get meaningful results, but we both struggle with some filters being set too high, and a considerable sensitivity to the top fraction of positive values; for IgG the filter threshold becomes much higher if a positive control is used instead of a negative one. The way to at least partly remedy this is by including the “ignore_white” parameter. What this does is to discard any value above this “white” in the calculations of the background. In this case, as we have already calculated the quantile intensities for the positive image, we can use these to improve the calculation of this threshold.
Here we run the optional EGFP channel intensityCutoff identification:
intensityCutoffGreen <- getIntensityCutoff(
imgDirs = list(posImage),
frameNum = 2,
ignore_white =
quantIntensities[[1]][, "Percent_90"][2]
)
intensityCutoffGreen
## [1] 0.012
Now, all the components are in place for the islify function to be run.
This is the most generalisable case of the function, that works well in instances with a high signal-to-noise ratio for the antibody marker. Examples where this works well are for GABAB and CASPR2 antibody detection.
result <- islify(
imgDirs = list(negImage, posImage),
imgNames = c("Neg", "Pos"),
frameNumFocus = 1,
sizeCutoff = sizeCutoff,
intensityCutoffFocus =
intensityCutoffRed,
truncLim =
quantIntensities[[1]][, "Percent_95"][1]
)
result
## intensityCutoff fractionOfAll_focus
## Neg 25 0.0000
## Pos 25 0.3637
Here, we get two results: first the intensityCutoff is reported back. In this case, it is not very useful, as we provided it before, but as one can have individualised thresholds for each file, it is important to get it. In cases with extremely high background, such as with fixed cell-based assays, there is a high noise flag that can turn an extra filtering step on. In this case, this second filter will also be reported back in the results. Finally, the central result is presented as the fraction of the total number of pixels that are IgG positive.
This function can in addition to these results also generate a diagnostic
image for each analysed image. These images look like this, with the red colors
indicating the selected islands:
Here, we add the information gathered about the EGFP as well, to further select the areas of analysis. This might not make very much sense in this very clean example, but in more noisy, normal-world data, it is often this cleanup step that makes this method advantageous compared to direct visual analysis.
result <- islify(
imgDirs = list(negImage, posImage),
imgNames = c("Neg", "Pos"),
frameNumFocus = 1,
frameNumReference = 2,
sizeCutoff = sizeCutoff,
intensityCutoffFocus =
intensityCutoffRed,
intensityCutoffReference =
intensityCutoffGreen,
truncLim =
quantIntensities[[1]][, "Percent_95"][1]
)
result
## intensityCutoff_ref intensityCutoff_focus fractionOfAll_ref
## Neg 12 25 0.5635667
## Pos 12 25 0.2366778
## fractionOfAll_focus fractionOfRef_focus
## Neg 0.0000000 0.0000000
## Pos 0.1902111 0.8036712
Here, the output contains information both about the reference and the focus; the intensity thresholds are included for both, as well as the fraction of the total pixels that are reference-coloured or focus-coloured. A combination of the two, namely the fraction of reference that are also focus-coloured is the crown of the whole thing.
And the positive image is in this case somewhat cleaner:
In this document, a typical analysis of cell-based assays for autoantibody screening is described. Other applications in similar fields, such as binding patterns to neurons are also possible to conceive and more intelligent filters or masks can certainly also be found. This attempt however seems to work for a number of tests in the field of autoimmune neurology autoantibody screening. If you have ideas for similar applications, or cannot find your file format, please reach out.
sessionInfo()
## R version 4.5.0 RC (2025-04-04 r88126)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 24.04.2 LTS
##
## Matrix products: default
## BLAS: /home/biocbuild/bbs-3.21-bioc/R/lib/libRblas.so
## LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.12.0 LAPACK version 3.12.0
##
## locale:
## [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
## [3] LC_TIME=en_GB LC_COLLATE=C
## [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
## [7] LC_PAPER=en_US.UTF-8 LC_NAME=en_US.UTF-8
## [9] LC_ADDRESS=en_US.UTF-8 LC_TELEPHONE=en_US.UTF-8
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=en_US.UTF-8
##
## time zone: America/New_York
## tzcode source: system (glibc)
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] islify_1.0.0 knitr_1.50 BiocStyle_2.36.0
##
## loaded via a namespace (and not attached):
## [1] Matrix_1.7-3 EBImage_4.50.0 jsonlite_2.0.0
## [4] compiler_4.5.0 BiocManager_1.30.25 ijtiff_3.1.3
## [7] Rcpp_1.0.14 stringr_1.5.1 bitops_1.0-9
## [10] jquerylib_0.1.4 png_0.1-8 strex_2.0.1
## [13] yaml_2.3.10 fastmap_1.2.0 lattice_0.22-7
## [16] dbscan_1.2.2 R6_2.6.1 generics_0.1.3
## [19] backports_1.5.0 BiocGenerics_0.54.0 htmlwidgets_1.6.4
## [22] checkmate_2.3.2 bookdown_0.43 fftwtools_0.9-11
## [25] bslib_0.9.0 autothresholdr_1.4.2 tiff_0.1-12
## [28] rlang_1.1.6 stringi_1.8.7 cachem_1.1.0
## [31] xfun_0.52 sass_0.4.10 cli_3.6.4
## [34] magrittr_2.0.3 digest_0.6.37 grid_4.5.0
## [37] locfit_1.5-9.12 rJava_1.0-11 lifecycle_1.0.4
## [40] S4Vectors_0.46.0 glue_1.8.0 evaluate_1.0.3
## [43] abind_1.4-8 stats4_4.5.0 RCurl_1.98-1.17
## [46] rmarkdown_2.29 tools_4.5.0 jpeg_0.1-11
## [49] RBioFormats_1.8.0 htmltools_0.5.8.1