Try   HackMD
tags: QuPath Fiji imageJ R

Immunohistochemistry image processing and analysis

  • Author: Lun-Hsien Chang
  • Date created: May 2020

Using the R function in next section to annotate all IHC images

  • Download the cell segmentation data file cell-seg-data-merged_CD8.txt from QIMR L drive L:/Lab_MarkS/lunC/work/Immunohistochemistry_images/data_output/AP_Exp108.1_PeterMac-lungCancer-CD8-PD1/analysis-results
  • Download the folder with IHC image files from QIMR L drive L:/Lab_MarkS/lunC/work/Immunohistochemistry_images/data_input/AshR/1. mIHF/Exp 108 210728/CD8 path
  • Create a new R script file. Copy the code chunk below and modify the path of input files and folders.
  • Run the source() function to load the R function to the working environment.
    • Modify the file path within the double quotes. Make sure backword slashes \ are replaced by forward slashes / when the path is copied from Windows address bar.
    • In R, place your cursor anywhere in the line with the source() and run this function by pressing the Ctrl and Enter keys at the same time.
  • Modify the values that will be taken by the following arguments in the function DrawAllIHCImagesTMAWithRectanglesInForm()
    • input.cell.seg.data.file.path= This is the full path of your cell segmentation file. Make sure the string value is within a pair of quotes.
    • input.images.folder.path= This is the full path of the folder with IHC image files to annotate. Do not end the path with / .
    • threshold.marker= Specify a threshold for positive marker measurement with a number.
    • threshold.confidence= Specify a threshold for confidence with a number.
    • rectangle.width.half= Specify 50% of the width of a square. This value is used to calculate the coordinates of the four points. rectangle.width.half=15 creates squares that are 30 by 30 pixels.
    • legend.location= Specify which corner you want your legend to be. Aceept four string values- "topright" "topleft" "bottomright" "bottomleft"
    • rectangle.color= Name the color of your rectangles. To use a different color from the default black, look up a color name in R An overview of color names in R
    • legend.text.line1= legend.text.line2= legend.text.line3= Allow a 3-line legned to be placed using the text you supply. Use line 1 for your project/study name. Use line 2 for your marker. Use line 3 for the thresholds.
    • legend.color= Name the color of the legend. To use a different color from the default black, look up a color name in R An overview of color names in R
    • legend.text.cex= Use a number to increase or decrease the font size of the legend. legend.text.cex=2.5 expands the size to 250%
    • output.folder.path= Create a new folder where the annotated image files will be exported. Add the folder path to the argument. Do not end the path with /
    • output.excluded.images.folder.path= Create another new folder where the unannotated image files will be exported. Add the folder path to the argument. Do not end the path with /
source("E:/Lab_MarkS/lunC_work/Immunohistochemistry_images/scripts/RFunction_annotate-all-IHC-images.R")

# Annotate CD8+
DrawAllIHCImagesTMAWithRectanglesInForm(input.cell.seg.data.file.path="E:/Lab_MarkS/lunC_work/Immunohistochemistry_images/data_output/AP_Exp108.1_PeterMac-lungCancer-CD8-PD1/analysis-results/cell-seg-data-merged_CD8.txt"
                                        ,input.images.folder.path="E:/Lab_MarkS/lunC_work/Immunohistochemistry_images/data_input/AshR/1. mIHF/Exp 108 210728/CD8 path"
                                        ,threshold.marker=5
                                        ,threshold.confidence=95
                                        ,rectangle.width.half=12.5
                                        ,legend.location="topright"
                                        ,rectangle.color="black"
                                        ,legend.text.line1="Project: Exp108"
                                        ,legend.text.line2="Marker: CD8+"
                                        ,legend.text.line3="Marker threshold: 5, confidence threshold: 95%"
                                        ,legend.color="black"
                                        ,legend.text.cex=2.5                                     ,output.folder.path="E:/Lab_MarkS/lunC_work/Immunohistochemistry_images/data_output/AP_Exp108.1_PeterMac-lungCancer-CD8-PD1/CD8_path_view"
                                        ,output.excluded.images.folder.path="E:/Lab_MarkS/lunC_work/Immunohistochemistry_images/data_output/AP_Exp108.1_PeterMac-lungCancer-CD8-PD1/CD8_path_view/excluded")
  • Download R script file annotate-IHC-images_Exp108v1.R with the code above from QIMR L drive L:/Lab_MarkS/lunC/work/Immunohistochemistry_images/scripts

A R function to annotate IHC images using a cell segmentation data file

  • Download the R script file RFunction_annotate-all-IHC-images.R from QIMR network drive L L:/Lab_MarkS/lunC/work/Immunohistochemistry_images/scripts
  • The input cell segmentation data file should be
    • A tab-separated file
    • Having at least five columns with headers in this order. The function identifies the columns by their positions, not by their names
      • column 1 : image file names
      • column 2 : X coordinates
      • column 3 : Y coordinates
      • column 4 : Marker measurement
      • column 5 : Confidence
  • The input image files can be in different formats such as .tif, .tiff, .jpg, .jpeg, and .png (not tested yet) *
# Check if required packages are already installed
if (!"dplyr" %in% rownames(installed.packages())) install.packages("dplyr")
if (!"foreach" %in% rownames(installed.packages())) install.packages("foreach")
if (!"doParallel" %in% rownames(installed.packages())) install.packages("doParallel")
if (!"magick" %in% rownames(installed.packages())) install.packages("magick")
if (!"graphics" %in% rownames(installed.packages())) install.packages("graphics")
if (!"grDevices" %in% rownames(installed.packages())) install.packages("grDevices")
if (!"tools" %in% rownames(installed.packages())) install.packages("tools")

# Load packages
library(dplyr) # to use %>% operator 
library(doParallel) # to use %dopar% operator

DrawAllIHCImagesTMAWithRectanglesInForm<- function( input.cell.seg.data.file.path="D:/googleDrive/copy-to-lunC_work/Merged_data/190529 batch analysis_lung cancer_cell_seg_data.txt"
                                                    ,column.position.image.file.name=1
                                                    ,column.position.x.cooridnate=2
                                                    ,column.position.y.cooridnate=3
                                                    ,column.position.marker.measurement=4
                                                    ,column.position.confidence=5
                                                    ,input.images.folder.path
                                                    ,threshold.marker=8
                                                    ,threshold.confidence=0
                                                    ,rectangle.width.half=12.5
                                                    ,legend.location="topright"
                                                    ,rectangle.color="black"
                                                    ,legend.text.line1="Project: demo"
                                                    ,legend.text.line2="Marker: CD8+"
                                                    ,legend.text.line3="Marker threshold: 8, confidence threshold: 0"
                                                    ,legend.color="black"
                                                    ,legend.text.cex=2.5
                                                    ,output.folder.path="D:/googleDrive/copy-to-lunC_work/CD8_annotated"
                                                    ,output.excluded.images.folder.path="E:/Lab_MarkS/lunC_work/Immunohistochemistry_images/data_output/AP_Exp108.1_PeterMac-lungCancer-CD8-PD1/CD8_path_view/excluded"){
  # Check file existence
  if(file.exists(input.cell.seg.data.file.path)!=TRUE){
    cat("Input file for cell segmentation data could not be found")
  } else {
    
    # Import cell segmentation data file
    data <- read.delim(file=input.cell.seg.data.file.path, header = TRUE, sep = "\t", stringsAsFactors = F) %>%
      dplyr::select(c( column.position.image.file.name
                       ,column.position.x.cooridnate
                       ,column.position.y.cooridnate
                       ,column.position.marker.measurement
                       ,column.position.confidence)) %>%
      dplyr::mutate(
        Confidence_percent= as.numeric(stringr::str_replace_all(string=.[,5]
                                                                ,pattern="%"
                                                                ,replacement=""))) # dim(data) 2110587 6
    
    data.subset <- data %>% dplyr::filter(data[,4] > threshold.marker & data[,5] > threshold.confidence) # dim(data.subset)  52173 6
    cat((nrow(data)-nrow(data.subset))/nrow(data)*100, "% data are filtered out by the marker threshold of ", threshold.marker, "and confidence threshold of", threshold.confidence)
  }
  
  if(dir.exists(input.images.folder.path)!=TRUE){
    cat("Input image file could not be found")
  } else{
    # Read input image file paths as a vector
    image.file.paths <- list.files(path = input.images.folder.path, full.names = TRUE) # length(image.file.paths) 104
    cat("There are",length(image.file.paths),"images in the folder")
    
    # Use image file name to look up data for the image in the cell segmentation data
    foreach::foreach(i=1:length(image.file.paths)
                     ,.combine = 'c'
                     ,.packages=c("foreach","dplyr")) %dopar% {
                       print(paste0("=========== Working on image ",i,"============"))
                       
                       # If data can be found, then subset. If data cannot be found, then copy the input image to the output
                       image.file.path <-image.file.paths[i]
                       image.file.name <-basename(image.file.path)
                       
                       # Use image file name to subset data. If no data can be found, copy the image file to the excluded folder
                       d <- data.subset %>% dplyr::filter(file.name==image.file.name) # dim(d) 99 6 # nrow(d)
                       if(nrow(d)==0){
                         file.copy( from = image.file.path
                                    ,to=file.path(output.excluded.images.folder.path, image.file.name)
                                    ,copy.date = TRUE
                         )
                       } else{
                         # Handle errors when the pattern fails to find the matched image file
                         # Calculate coordinates for four points of rectangles
                         tryCatch({
                           # Read input image as a magick-image object
                           image <- magick::image_read(path=image.file.path) # class(image) "magick-image"
                           
                           # Draw the imported image. 
                           image.drew <- magick::image_draw(image)
                           
                           # Create pixel coordinates for rectangles
                           rect.x.left <- d[,2] - rectangle.width.half # length(rect.x.left) 556
                           rect.x.right <- d[,2] + rectangle.width.half # length(rect.x.right) 556
                           rect.y.top <- d[,3] - rectangle.width.half # length(rect.y.top) 556
                           rect.y.bottom <- d[,3] + rectangle.width.half # length(rect.y.bottom) 556
                           
                           graphics::legend(legend.location #"topleft"
                                            ,legend = c(  legend.text.line1
                                                          ,legend.text.line2
                                                          ,stringr::str_wrap(string=legend.text.line3, width=80)
                                                          ,paste0(rectangle.width.half*2, " x ", rectangle.width.half*2, " pixels")
                                            )
                                            ,bty = "n"
                                            ,pt.cex = legend.text.cex
                                            ,cex = legend.text.cex
                                            ,text.col = c(legend.color))
                           
                           
                           ## Draw rectangles for individual stained cells
                           graphics::rect( xleft= rect.x.left
                                           ,ybottom=rect.y.bottom
                                           ,xright=rect.x.right
                                           ,ytop=rect.y.top
                                           ,col = NA # color(s) to fill or shade the rectangle(s) with. The default NA (or also NULL) means do not fill, i.e., draw transparent rectangles, unless density is specified.
                                           ,border = rectangle.color # color for rectangle border(s).
                                           ,lty = par("lty")
                                           ,lwd = 2)
                           
                           # Close the image device
                           grDevices::dev.off()
                           #--------------------------
                           # Export the painted image 
                           #--------------------------
                           # Make output file name
                           output.image.file.name <- paste("annotated", basename(image.file.path)
                                                           ,sep = "_") # ,TMA.core.ID
                           
                           # Make output file path without file extension
                           output.image.file.path <- file.path(output.folder.path, output.image.file.name)
                           
                           # Make output file name without file extension
                           output.image.file.name.ext.rm <- tools::file_path_sans_ext(output.image.file.name)
                           
                           if(nchar(output.image.file.path) < 260){
                             magick::image_write( image= image.drew
                                                  ,path= output.image.file.path)
                             
                           } else {
                             # Truncate file name to 259 character long from the end
                             cut.length <- nchar(output.image.file.path) - 259 # 4
                             end.position <- nchar(output.image.file.name.ext.rm) -  cut.length
                             
                             output.image.file.path.shortened <- file.path(output.folder.path
                                                                           ,paste0( stringr::str_sub( string= tools::file_path_sans_ext(output.image.file.name)
                                                                                                      ,start = 1
                                                                                                      ,end= end.position)
                                                                                    # Append file extension to shortened file name
                                                                                    ,"."
                                                                                    ,tools::file_ext(output.image.file.name)
                                                                           )
                             ) 
                             magick::image_write( image= image.drew
                                                  ,path= output.image.file.path.shortened)
                           } 
                         }, error=function(e){cat("ERROR :",conditionMessage(e), "\n")} 
                         ) # End tryCatch() function
                       }
                     } # End %dopar%
  } # End else
} # End the main function

Using the R function in next section to summarise cell segmentation data to counts of single, double positive cells

  • Download the cell segmentation data file AP_Exp108.1_PeterMac-lungCancer-CD8-PD1_cell-seg-data-merged_CD8_PD1.tsv from QIMR L drive L:/Lab_MarkS/lunC/work/Immunohistochemistry_images/data_output/AP_Exp108.1_PeterMac-lungCancer-CD8-PD1/analysis-results
  • Create a new R script file. Copy the code chunk and modify the paths of input and output files and folders
  • Run the source() function to load the R function to the working environment.
    • Modify the file path within the double quotes. Make sure backword slashes \ are replaced by forward slashes / when the path is copied from Windows address bar.
    • In R, place your cursor anywhere in the line with the source() and run this function by pressing the Ctrl and Enter keys at the same time.
  • Modify the values that will be taken by the following arguments in the function SummariseTwoMarkersCellSegmentationDataInForm()
    • file.path.cell.seg.data.file= This is the full path of your cell segmentation file. Make sure the string value is within a pair of quotes.
    • input.images.folder.path= This is the full path of the folder with IHC image files to annotate. Do not end the path with / .
    • marker.1.info.list= Specify a list of three values for marker 1 (1) Name your marker 1 (e.g., CD8), (2) a threshold that dichotimises marker measurement values into negative or positive, (3) a threshold that dichotimises confidence.
    • marker.2.info.list= Specify a list of three values for marker 2 (1) Name your marker 1 (e.g., CD226), (2) a threshold that dichotimises marker measurement values into negative or positive, (3) a threshold that dichotimises confidence.
    • output.folder.path= Create a new folder where the summary data will be exported. Add the folder path to the argument. Do not end the path with /
source("E:/Lab_MarkS/lunC_work/Immunohistochemistry_images/scripts/RFunction_summarise-cell-segmentation-files.R")
SummariseTwoMarkersCellSegmentationDataInForm( file.path.cell.seg.data.file="E:/Lab_MarkS/lunC_work/Immunohistochemistry_images/data_output/AP_Exp108.1_PeterMac-lungCancer-CD8-PD1/analysis-results/AP_Exp108.1_PeterMac-lungCancer-CD8-PD1_cell-seg-data-merged_CD8_PD1.tsv"
                                               ,marker.1.info.list=list(c("CD8", 1.8, 98))
                                               ,marker.2.info.list=list(c("CD226", 3, 98))                                               ,output.folder.path="E:/Lab_MarkS/lunC_work/Immunohistochemistry_images/data_output/AP_Exp109.1_PeterMac-lungCancer-CD8-CD226/analysis-results")
  • The R code above is in the R script file annotate-IHC-images_Exp108v1.R at QIMR L drive L:/Lab_MarkS/lunC/work/Immunohistochemistry_images/scripts

A R function to summarise cell segmentation data to counts of single, double positive cells

  • Location of the script file on QIMR network drive L L:/Lab_MarkS/lunC/work/Immunohistochemistry_images/scripts/RFunction_summarise-cell-segmentation-files.R
  • The input cell segmentation data file should be
    • A tab-separated file
    • Having at least 4 columns with headers in this order. The function identifies the columns by their positions, not by their names
      • column 1 : A grouping variable that uniquely identifies TMA core location
      • column 2 : Confidence variable with string values (e.g., 95%)
      • column 3 : measurement of marker 1
      • column 4 : measurement of marker 2
# Check if required packages are already installed
if (!"dplyr" %in% rownames(installed.packages())) install.packages("dplyr")
if (!"tools" %in% rownames(installed.packages())) install.packages("tools")

# Load packages
library(dplyr) # to use %>% operator

SummariseTwoMarkersCellSegmentationDataInForm<- function( file.path.cell.seg.data.file="E:/Lab_MarkS/lunC_work/Immunohistochemistry_images/data_output/AP_Exp108.1_PeterMac-lungCancer-CD8-PD1/analysis-results/AP_Exp108.1_PeterMac-lungCancer-CD8-PD1_cell-seg-data-merged_CD8_PD1.tsv"
                                                          ,column.position.grouping.variable=1
                                                          ,column.position.confidence=2
                                                          # Measurement for marker 1 should be at column 3
                                                          ,column.position.marker.1=3
                                                          # Measurement for marker 2 should be at column 4
                                                          ,column.position.marker.2=4
                                                          ,marker.1.info.list=list(c("CD8", 1.8, 98))
                                                          ,marker.2.info.list=list(c("CD226", 3, 98)) # Name marker, threshold, confidence threshold
                                                          ,output.folder.path="E:/Lab_MarkS/lunC_work/Immunohistochemistry_images/data_output/AP_Exp109.1_PeterMac-lungCancer-CD8-CD226/analysis-results"){
  # Check file existence
  if(file.exists(file.path.cell.seg.data.file)!=TRUE){
    cat("Input file for cell segmentation data could not be found")
  } else {
    
    # Add wanted column positions to a vector
    column.positions <- as.numeric(c( column.position.grouping.variable
                                     ,column.position.confidence
                                     ,column.position.marker.1
                                     ,column.position.marker.2
                                     )
                                   )
    
    # Import cell segmentation data file
    cat("Reading these columns by position from the input data file into R","\n"
        ,column.positions,"\n")
    
    # Import data
    varname.marker.1 <- marker.1.info.list[[1]][1]
    varname.marker.2 <- marker.2.info.list[[1]][1]
    
    data <- read.delim( file=file.path.cell.seg.data.file
                        ,header = TRUE
                        ,sep = "\t"
                        ,stringsAsFactors = F
                        #,nrows = 20000
                        ) %>%
      dplyr::select(column.positions) %>%
      # Rename columns by positions
      dplyr::rename( grouping.variable = 1
                     ,!!varname.marker.1 := 3
                    ,!!varname.marker.2 := 4 ) %>%
      dplyr::mutate(
      # Create numeric confidence from string variable confidence
      confidence_percent= as.numeric(stringr::str_replace_all(string=.[,2]
                                                               ,pattern="%"
                                                               ,replacement=""))) %>%
      # Remove string column confidence
      dplyr::select(-2) %>%
      # Reorder columns
      dplyr::select(grouping.variable, confidence_percent, everything()) # dim(data) 2136791 4
    
    #----------------------------------------------------
    # Specify filtration conditions as expression objects
    #----------------------------------------------------
    measurement.threshold.marker.1 <- as.numeric(marker.1.info.list[[1]][2]) # marker.1.info.list[[1]][2]
    confidence.threshold.marker.1 <-  as.numeric(marker.1.info.list[[1]][3])

    measurement.threshold.marker.2 <- as.numeric(marker.2.info.list[[1]][2])
    confidence.threshold.marker.2 <- as.numeric(marker.2.info.list[[1]][3])
    
    # Set thresholds for marker 1 (column 3) and confidence (column 2)
    filtration.conditions.single.positive.marker.1 <- expression(.[,3] > measurement.threshold.marker.1 & 
                                                                 .[,2] > confidence.threshold.marker.1) # class(filtration.conditions.single.positive.marker.1) [1] "expression"
    
    # Set thresholds for marker 2 (column 5)and confidence (column 6)
    filtration.conditions.single.positive.marker.2 <- expression(.[,4] > measurement.threshold.marker.2 & 
                                                                   .[,2] > confidence.threshold.marker.2)
    # Set thresholds for double positive cells
    filtration.conditions.double.positive <- expression(.[,3] > measurement.threshold.marker.1 & 
                                                          .[,2] > confidence.threshold.marker.1 & 
                                                          .[,4] > measurement.threshold.marker.2 & 
                                                          .[,2] > confidence.threshold.marker.2)
    
    # Subset single positive cells for marker 1
    data.subset.single.positive.marker.1 <- data %>% 
      dplyr::filter(eval(filtration.conditions.single.positive.marker.1)) %>%
      dplyr::mutate(cell.status=paste0(marker.1.info.list[[1]][1],"+")) # dim(data.subset.single.positive.marker.1) 29226 5
    
    cat("Subsetting single positive cells based on"
        ,marker.1.info.list[[1]][1]
        ,"threshold of"
        , measurement.threshold.marker.1
        , "and confidence threshold of"
        , confidence.threshold.marker.1,"\n"
        ,(nrow(data)-nrow(data.subset.single.positive.marker.1))/nrow(data)*100
        ,"% data are filtered out","\n")
    
    # Subset single positive cells for marker 2
    data.subset.single.positive.marker.2 <- data %>% 
      dplyr::filter(eval(filtration.conditions.single.positive.marker.2))%>% 
      dplyr::mutate(cell.status=paste0(marker.2.info.list[[1]][1],"+")) # dim(data.subset.single.positive.marker.2) 40777 7
    
    cat("Subsetting single positive cells based on"
        ,marker.2.info.list[[1]][1]
        ,"threshold of"
        , measurement.threshold.marker.2
        , "and confidence threshold of"
        , confidence.threshold.marker.2,"\n"
        ,(nrow(data)-nrow(data.subset.single.positive.marker.2))/nrow(data)*100
        ,"% data are filtered out","\n")
    
    # Subset double positive cells
    data.subset.double.positive <- data %>%
      dplyr::filter(eval(filtration.conditions.double.positive))%>% 
      dplyr::mutate(cell.status=paste0( marker.1.info.list[[1]][1],"+"
                                       ,marker.2.info.list[[1]][1],"+"))# dim(data.subset.double.positive) 9501 7
    
    cat("Subsetting double positive cells based on"
        ,marker.1.info.list[[1]][1]
        ,"threshold of"
        , measurement.threshold.marker.1
        ,marker.2.info.list[[1]][1]
        ,"threshold of"
        , measurement.threshold.marker.1
        , "confidence threshold of"
        , confidence.threshold.marker.1
        ,"&" , confidence.threshold.marker.2,"\n"
        ,(nrow(data)-nrow(data.subset.double.positive))/nrow(data)*100
        ,"% data are filtered out","\n")  
    
    #-----------------------------------------
    # Count single positive cells per by group
    #-----------------------------------------
    # Get a list of TMA.core.location
    summary.data <- data %>% 
      dplyr::group_by(grouping.variable) %>%
      dplyr::count(name = "number.cells") # dim(summary.data) 553 2
      
    summary.marker.1 <- data.subset.single.positive.marker.1 %>%
      dplyr::group_by(grouping.variable) %>%
      # Add suffix _p for single positive cells
      dplyr::count(name = paste0(marker.1.info.list[[1]][1],"_p")) # dim(summary.marker.1) 424 2
    
    summary.marker.2 <- data.subset.single.positive.marker.2 %>%
      dplyr::group_by(grouping.variable) %>%
      # Add suffix _p for single positive cells
      dplyr::count(name = paste0(marker.2.info.list[[1]][1],"_p")) # dim(summary.marker.2) 504 2
    
    summary.double.positive <- data.subset.double.positive %>%
      dplyr::group_by(grouping.variable) %>%
      # Add suffix _p for single positive cells
      dplyr::count(name = paste0(marker.1.info.list[[1]][1],"p"
                                 ,marker.2.info.list[[1]][1],"p")) # dim(summary.double.positive) 317 2
    # Combine all summary data above
    summary.all <- list( summary.data
                        ,summary.marker.1
                        ,summary.marker.2
                        ,summary.double.positive) %>%
      purrr::reduce(dplyr::full_join, by=c("grouping.variable")) # dim(summary.all) 533 5
    
    # Change attribute class to data.frame
    # class(summary.all) # [1] "grouped_df" "tbl_df"     "tbl"        "data.frame"
    attributes(summary.all)$class <- "data.frame"
    
    # Calculate ratio as number of double positive divided by number of CD8+
    column.name.ratio <- paste0(names(summary.marker.2)[2],"_ratio")
    summary.all[[column.name.ratio]] <- summary.all[,5]/summary.all[,3]
    
    # Replace NA with 0
    summary.all[is.na(summary.all)] <- 0
    
    # Export summary data as a file
    write.table( x=summary.all
                 ,file = file.path(output.folder.path, "summary-of-cell-seg-data_single-positive_double-positive_ratio.tsv")
                 ,sep = "\t"
                 ,col.names = TRUE
                 ,quote = FALSE
                 ,row.names = FALSE)
    cat("Exported summary data file at","\n"
        ,file.path(output.folder.path, "summary-of-cell-seg-data_single-positive_double-positive_ratio.tsv"),"\n")
    
  } # End the else 
} # End the main function

Annotate tissue and detect DAB in QuPath

CD39

Create a new empty folder at
C:\Lab_MarkS\lunC_work\Immunohistochemistry_images\QuPath-projects\QuPath-project_Exp91-BRAFRES1-RBWHMM_CD39

Create a new QuPath project and select the folder above as the project folder
File> Project > create project

Add images to the project from
L:\Lab_MarkS\AshR\CD39 project images (all)\exp91_CD39 abcam M3R repeat

Create class
Annotations > right-click in the class panel > Add class > Name it as "tissue" > change color to "Red" The pixel classifier tissueClassifier adds red to tissue according the color specified by tissue=red
Annotations > right-click in the class panel > Add class > Name it as "DAB-positive" > change color to "Lime". The pixel classifier DAB-measurement adds green to DAB according the color specified by DAB-positive=green

Following the project description at L:\Lab_MarkS\AshR\5. QuPath Analysis\Analysis01.1 - MM_CD39 DAB Measure_AP210401\Description analysis01.1.txt, find out which the json file names used as the pixel classifiers. Here they are
findtissue vh H G 1 0.05 tissue ignore e.json
vh dab g 0.25 pos neg ash.json

Copy the source [pixel_classifiers folder](L:/Lab_MarkS/AshR/5. QuPath Analysis/Analysis01.1 - MM_CD39 DAB Measure_AP210401/classifiers/pixel_classifiers) to the project folder

Copy an existing scripts folder to the project scripts folder

Modify the script file

//Change the name of json files from
createAnnotationsFromPixelClassifier("findtissue_vh_H_G_1_0.05_tissue_ignore_e", 10000.0, 10000.0) //threshold 0.05
// to
createAnnotationsFromPixelClassifier("findtissue vh H G 1 0.05 tissue ignore e", 10000.0, 10000.0) //threshold 0.05

//from 
addPixelClassifierMeasurements("VH_DAB_G_0.5_POS_NEG_JASON2", "VH_DAB_G_0.5_POS_NEG_JASON2")
createDetectionsFromPixelClassifier("VH_DAB_G_0.5_POS_NEG_JASON2", 10.0, 10.0)
//to

Run the script
Script Editor> Run > Run for project > select all images


Classifying pixels with 2 pixel classifiers (manually)

  1. Create a project and add images
  2. load a pixel classifier

1

  1. Select tissueClassifier (/classifiers/pixel_classifiers/tissueClassifier.json)

2

  1. Apply the classifier to full image

3

  1. Specify hole size as 1000

    4

  2. Export renderred RGB

    5

  3. exported image

    step 1 to 6

  4. Delete annotations

    6

  5. load another pixel classifer

7

  1. Apply the classifier to full image

8

  1. Specify hole size as 1000

9

  1. Export renderred RGB

10

  1. Exported image as

step 9 to 12


Classifying pixels with 2 pixel classifiers (Tam's script)

  1. Run the script on 1 image

  1. exported image


Merge 3 channels in Fiji

Problem: THUNDER Imaging Systems generates images in grayscale. You would like to combine these grayscale images to single colored images

Data:
L:\Lab_MarkS\lunC\Immunohistochemistry_images\data_input\Exp59_HNSCC-CD226-ratio-201014-Rescanned\HNSCC_TMA_A_1core
HNSCC A Re-scan_1B_ICC Merged_RAW_ch00.tif
HNSCC A Re-scan_1B_ICC Merged_RAW_ch01.tif
HNSCC A Re-scan_1B_ICC Merged_RAW_ch02.tif

Solution:

Drag and drop a group of 3 images to Fiji

imageJ_merge-channels_01

Select channels

imageJ_merge-channels_02_select-channels

Save your merged image

imageJ_merge-channels_03_save-as


Merge channels by a script

Problem: You have a large number of grayscale images to merge. You don't enjoy clicking your mouse multiple times.

Data:
L:\Lab_MarkS\lunC\Immunohistochemistry_images\data_input\Exp59_HNSCC-CD226-ratio-201014-Rescanned\HNSCC_TMA_A_1core
HNSCC A Re-scan_1B_ICC Merged_RAW_ch00.tif
HNSCC A Re-scan_1B_ICC Merged_RAW_ch01.tif
HNSCC A Re-scan_1B_ICC Merged_RAW_ch02.tif

Solution:
Move image files to a new folder if non-image
Run a imageJ macro script file (C:\Lab_MarkS\lunC\Immunohistochemistry_images\scripts\merge-red-green-blue-channels.ijm)

Plugins > Macro > Edit > Select the merge-red-green-blue-channels.ijm file

imageJ_merge-channels_04_plugins-edit-macro

Run the script by clicking the Run tab or Ctrl+R

imageJ_merge-channels_05_run-macro

Enter file suffixes for blue, green and red channels

imageJ_merge-channels_06_enter-file-suffixes

Select the source image folder

imageJ_merge-channels_07_select-input-folder

Select a folder to save merged images

imageJ_merge-channels_08_select-output-folder

This error occurs if the number of elements in the folder is not a multiple of 3

imageJ_merge-channels_09_error

Channel merging successfully completed

imageJ_merge-channels_10_task-completed

//Clear the log window
print("//Clear");

//Create a popup window that takes input from users
Dialog.create("Enter file name suffixes that correspond to their channels");
  Dialog.addString("Blue Suffix:", "00");
  Dialog.addString("Green Suffix:", "01");
  Dialog.addString("Red Suffix:", "02");
 
  Dialog.show();
  blueSuffix = Dialog.getString() + ".";
  print("blueSuffix="+blueSuffix);
  greenSuffix = Dialog.getString() + ".";
  print("greenSuffix="+greenSuffix);
  redSuffix = Dialog.getString() + ".";
  print("redSuffix="+redSuffix);
  //Select the folder of source images  
  input_directory = getDirectory("Choose Source Directory");  
  list = getFileList(input_directory);
  //Select a folder to save images
  output_directory = getDirectory("Choose Save Directory");
  setBatchMode(true);
  n = list.length;
  print("The number of image files is "+n);
  if ((n%3)!=0)
    exit("The number of files must be a multiple of 3");
      stack = 0;
      first = 0;
      for (i=0; i<n/3; i++) {
      	  print("====================== Iternation "+i+"======================");
          showProgress(i+1, n/3);
          blue="?"; 
          print("blue="+blue);
          green="?"; 
          print("green="+green);
          red="?";
          print("red="+red);
          for (j=first; j<first+3; j++) {
              if (indexOf(list[j], blueSuffix)!=-1)
                  blue = list[j];
              if (indexOf(list[j], greenSuffix)!=-1)
                  green = list[j];    
              if (indexOf(list[j], redSuffix)!=-1)
                  red = list[j];
          }
		  open(input_directory+blue);
		  print("Opening blue channel image from "+input_directory+blue);
          open(input_directory+green);
          print("Opening green channel image from "+input_directory+green);
          open(input_directory+red);
          print("Opening red channel image from "+input_directory+red);
          
          run("Merge Channels...", "c1=["+red+"] c2=["+green+"] c3=["+blue+"] create");
          index = indexOf(blue, blueSuffix);
          print("index="+index);
          //Create output file names using input file name excluding the last 9 characters (e.g., _ch00.tif) and adding suffix "_channels-merged"
		  name = substring(blue, 0, lengthOf(blue)-9)+"_channels-merged";
		  print("The merged channels will be saved as ", name+".tiff"); 
          saveAs("tiff", output_directory+name);
          //Increment the first variable by 3 to work on the next group of 3 images
          first += 3;
          //Close all the opened images
          close();
          setBatchMode(false);
          }
showMessage("Channels merging completed");

Check the MetaData folder

Problem: What if you don't know the file suffixes of channels

Solution?: Check *_Properties.xml file under the MetaData folder

MetaData_01_open-properties.xml-file