1 Introduction

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.

2 Preparatory parts of workflow

2.1 Installation

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")

2.2 Example data description

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)

2.3 getQuantileIntensities usage

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:

  1. these files are seemingly normalised to the 0-1 range.
    • Often, this is done in an RGB-compliant manner, so that each files lowest and highest values are set to 0 and 1, but it is much preferred if files with the original intensities are saved, as this will allow us to make more accurate between-image comparisons, which is of course the core point of this package.
    • In this specific case, the files have been normalised together, and therefore they can still be compared with the same intensity thresholds, etc.
  2. We can furthermore learn that the file contains three colors and we can also see that this file seems to have reasonably balanced intensities in the three different colors, which all show very skewed distributions.
    • A point worth making here and repeatedly is that the colors can have any order within the data. As multiple file formats are accepted, and as the metadata containing the information about said order differs substantially between the formats, it is up to the user to provide the information about which frame in the files that contain which color. Currently, only three colors are accepted, and as the visual output are RGB-based png files, red, green and blue are the only supported colors. If this restricts you, then please reach out and we can discuss workarounds and updates to the package.
    • Therefore, NB: If the order of the colors in the file in fact is DAPI for nuclear staining (blue), then phycoerythrin for IgG staining (red) and finally fusion-protein EGFP (green), and the user provides the color order Red-Green-Blue, then the nuclei will look red, the IgG green and the EGFP blue in the output, so this can lead to severe misinterpretation. So make sure to check this order before, e.g. by investigating one or a few files briefly with ImageJ or simply by comparing positive and negative controls with the getQuantileIntensities function.

2.4 Use saveImage for quality control and sanity checks

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: Image Name 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.

3 islify support functions

Now, we go into the pre-islify phase, where the nuclear size threshold as well as the intensity threshold(s) are defined.

3.1 getSizeCutoff

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

3.2 getIntensityCutoff for the antibody (and EGFP) staining(s)

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

4 Islify function in its two shapes

Now, all the components are in place for the islify function to be run.

4.1 Running islify for samples lacking co-expression markers

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: Image Name

4.2 Running islify including a reference color

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: Image Name

5 Conclusion

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.

6 Session information

sessionInfo()
## R version 4.5.0 beta (2025-04-02 r88102)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 24.04.2 LTS
## 
## Matrix products: default
## BLAS:   /home/biocbuild/bbs-3.22-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.1.0     knitr_1.50       BiocStyle_2.37.0
## 
## loaded via a namespace (and not attached):
##  [1] Matrix_1.7-3         EBImage_4.51.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.55.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.47.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.9.0    htmltools_0.5.8.1