inst/shiny-Viridot/theapp/server.R

#~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
# March 4, 2019: Server for the R plaque counter in Shiny
#~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*

# for importing/exporting files and saving directory/folder names in shiny
library(shinyFiles)
# shiny
library(shiny)
# selections for how the shiny user interface looks
library(shinythemes)

# for plaque counting
require(EBImage)
# For mixedsort
require(gtools)
# for fileextension search
require(tools)
#
require(drc)
require(Hmisc)


# suppress warnings
options(warn = -1)

#~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
#  make colors transparent
#~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
makeTransparent <- function(someColor, alpha = 100)
{
  newColor <- col2rgb(someColor)
  apply(newColor, 2, function(curcoldata) {
    rgb(
      red = curcoldata[1],
      green = curcoldata[2],
      blue = curcoldata[3],
      alpha = alpha,
      maxColorValue = 255
    )
  })
}

#~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
#  Convert saturation: this function is needed for the Viridot plaque counter
#~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*

convert.saturation <- function(picture, print = "saturation") {
  size <- dim(picture)[1]
  
  red <- as.vector(imageData(picture[, , 1]))
  green <- as.vector(imageData(picture[, , 2]))
  blue <- as.vector(imageData(picture[, , 3]))
  
  colmat <- rbind(red, green, blue)
  colhsv <- rgb2hsv(colmat)
  
  hue <- matrix(colhsv[1, ], size, size)
  saturation <- matrix(colhsv[2, ], size, size)
  value <- matrix(colhsv[3, ], size, size)
  
  if (print == "saturation")
    return(saturation)
  if (print == "hue")
    return(hue)
  if (print == "value")
    return(value)
}

#~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
#  Circle cut: a function needed for the Viridot plaque counter for cutting off the edges of wells
#~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*

cir.cut <- function (image,
                     radiuscut = 40,
                     insert.value = 0) {
  x <- length(image[1, ]) / 2
  y <- x
  nv <- x * 2
  radius <- x - radiuscut
  angle.inc <- 2 * pi / nv
  angles <- seq(0, 2 * pi - angle.inc, by = angle.inc)
  
  for (circle in 1:length(radius)) {
    xv <- round(cos(angles) * radius[circle] + x, digits = 0)
    yv <- round(sin(angles) * radius[circle] + y, digits = 0)
  }
  
  circlecut.image <- image
  
  for (i in 1:x) {
    allna <- yv[i]:(y * 2)
    thesex <- xv[i]:xv[i + 1]
    circlecut.image[thesex, allna] <- insert.value
  }
  
  for (i in x:(x * 2 - 1)) {
    allna <- 1:yv[i]
    thesex <- (xv[i]):(xv[i + 1])
    circlecut.image[thesex, allna] <- insert.value
  }
  
  circlecut.image[c(1:radiuscut, (nv - radiuscut):nv), ] <-
    insert.value
  
  
  return(circlecut.image)
  
}





#~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
# Linear approximation for estimating curve
#~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
estimate.x.dose.value <- function(predicted.v,
                                  dilvalues.v,
                                  set.of.dil.values.v) {
  predicted.v.sub <- predicted.v
  ind.min.1 <-  which.min(abs(predicted.v.sub - dilvalues.v))
  predicted.v.sub[ind.min.1] <- 9999999
  ind.min.2 <-  which.min(abs(predicted.v.sub - dilvalues.v))
  
  x.y.pred <-
    data.frame(set.of.dil.values.v, predicted.v)[c(ind.min.1, ind.min.2), ]
  
  M.sl = (x.y.pred$predicted.v[2] - x.y.pred$predicted.v[1]) /
    (x.y.pred$set.of.dil.values.v[2] - x.y.pred$set.of.dil.values.v[1])
  
  B.sl <-
    x.y.pred$predicted.v[1] - M.sl * x.y.pred$set.of.dil.values.v[1]
  # B.sl <- x.y.pred$predicted.v[2] - M.sl*x.y.pred$set.of.dil.values.v[2]
  
  return((dilvalues.v - B.sl) / M.sl)
  
}





#~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
#  Shiny server
#~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
shinyServer(function(input, output, session) {
 
   if (!exists("volumes")) {
    volumes = "~/"
    names(volumes) <- "home"
  }
  #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
  # Find file of previous counter settings and load those settings
  #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
  shinyFileChoose(
    input,
    'load_settings',
    roots = volumes,
    session = session,
    restrictions = system.file(package = 'base'),
    filetypes = c('csv')
  )
  

  observeEvent(input$load_settings, {

    err.rt <-
      try(read.csv(
        file = as.character(
          parseFilePaths(volumes, input$load_settings)$datapath[1]
        )),silent=T)
    

    if (class(err.rt) == "try-error") {
      output$indicate_load_settings <-
        renderText("Waiting for you to import your file.")
    }

    
    if (class(err.rt) != "try-error") {
        
      df <-
        read.csv(file = as.character(parseFilePaths(volumes, input$load_settings)$datapath[1]))
    
    if (is.null(df$input.contrast.background)) {
    output$indicate_load_settings <-
      renderText("You have tried to read in a csv file that is in the wrong format (it does not contain a column named contrast.background).")
    }
    
    if (!is.null(df$input.contrast.background)) {

    if (length(df$user_names_output_files) == 0)
      df$user_names_output_files <-
        df$input.user_names_plaque_count_tables
 
    if (length(df$input.plaque.sizes) == 0)
      df$input.plaque.sizes <- "No"
    
    
    if (df$input.counter.settings == "Manual")
      df$input.counter.settings <- "User-specified"
   
    
    updateSliderInput(session,
                      inputId = 'plaque.sizes',
                      value = df$input.plaque.sizes)
    
    updateSliderInput(session,
                      inputId = 'contrast.background',
                      value = df$input.contrast.background)
    updateSliderInput(session,
                      inputId = 'contrast.plaque',
                      value = df$input.contrast.plaque)
    updateSliderInput(session,
                      inputId = 'threshold.difference',
                      value = df$input.threshold.difference)
    updateSliderInput(session,
                      inputId = 'theshold.window.size',
                      value = df$input.theshold.window.size)
    updateSliderInput(session,
                      inputId = 'dilation.size',
                      value = df$input.dilation.size)
    updateSliderInput(session,
                      inputId = 'plaque.overlap.tolerance',
                      value = df$input.plaque.overlap.tolerance)
    updateSliderInput(session,
                      inputId = 'min.pixel.counted',
                      value = df$input.min.pixel.counted)
    updateSliderInput(session,
                      inputId = 'max.pixel.counted',
                      value = df$input.max.pixel.counted)
    updateSliderInput(session,
                      inputId = 'plate.96.wells',
                      value = df$input.plate.96.wells)
    updateSliderInput(session,
                      inputId = 'button.save.tables',
                      value = df$input.button.save.tables)
    updateTextInput(session,
                    inputId = 'user_names_output_files',
                    value = df$input.user_names_output_files)
    updateSliderInput(session,
                      inputId = 'print.image',
                      value = df$input.print.image)
    
    updateSliderInput(
      session,
      inputId = 'show.what.is.done.at.each.step',
      value = df$input.show.what.is.done.at.each.step
    )
    updateSliderInput(session,
                      inputId = 'counter.settings',
                      value = df$input.counter.settings)
    updateSliderInput(session,
                      inputId = 'light.setting',
                      value = df$input.light.setting)
    updateSliderInput(session, inputId = 'blur', value = df$input.blur)
    updateSliderInput(session,
                      inputId = 'remove.lines',
                      value = df$input.remove.lines)
    updateSliderInput(session,
                      inputId = 'cut.well.edges',
                      value = df$input.cut.well.edges)
    updateSliderInput(
      session,
      inputId = 'insert.value.for.pixels.outside.well',
      value = df$input.insert.value.for.pixels.outside.well
    )
    
    output$indicate_load_settings <-
      renderText("Saved parameters imported.")
    }
    }
    
  })
  
  
  #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
  # Set directory where to look for plate folders to analyze
  #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
  shinyDirChoose(
    input,
    'set_root_directory',
    roots = volumes,
    session = session,
    restrictions = system.file(package = 'base')
  )
  output$print_set_root_directory <-
    renderPrint({
      parseDirPath(volumes, input$set_root_directory)
    })
  
  #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
  # Based on the directory selected for where the plate folders are, select the plates to analyze
  #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
  
  observeEvent(input$set_root_directory, {
    
      updateSelectInput(session,
                      inputId = 'select_plates_plaque_counter',
                      choices = c(mixedsort(list.files(
                        parseDirPath(volumes, input$set_root_directory)
                      ))))
    
  })
  
  #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
  # Based on the plates folders, select the wells to analyze
  #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
  observeEvent(input$select_plates_plaque_counter, {
    updateSelectInput(session,
                      inputId = 'wells',
                      choices = c("all", mixedsort(list.files(
                        paste(
                          parseDirPath(volumes, input$set_root_directory),
                          "/",
                          input$select_plates_plaque_counter,
                          sep = ""
                        )
                      ))))
    
  })
  
  #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
  # Select where to save outlined images as well as all tables.
  # THe program will automatically make a new directory specific to the plate you are counting that contains outlined images and plaque size estimates, and then saves plaque count tables directly to directory selected here
  #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
  shinyDirChoose(
    input,
    'save_output_files_dir',
    roots = volumes,
    session = session,
    restrictions = system.file(package = 'base')
  )
  
  output$print_save_output_files_dir <-
    renderPrint({
      parseDirPath(volumes, input$save_output_files_dir)
    })
  
  #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
  # Select where to save plaque counter parameter settings
  #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
  shinyDirChoose(
    input,
    'choose_dir_save_settings',
    roots = volumes,
    session = session,
    restrictions = system.file(package = 'base'),
    filetypes = "csv"
  )
  output$print_choose_dir_save_settings <-
    renderPrint({
      parseDirPath(volumes, input$choose_dir_save_settings)
    })
  
  observeEvent (input$save_settings,  {
    updateTextInput(session,
                    inputId = 'unique.settings.name',
                    value = input$unique.settings.name)
    
    print.settings <-  data.frame(
      input$plaque.sizes,
      input$contrast.background,
      input$contrast.plaque,
      input$threshold.difference,
      input$theshold.window.size,
      input$dilation.size,
      input$plaque.overlap.tolerance,
      input$min.pixel.counted,
      input$max.pixel.counted,
      "button_set_directory_plaque_counter_val",
      input$plate.96.wells,
      input$button.save.tables,
      input$user_names_output_files,
      input$print.image,
      input$show.what.is.done.at.each.step,
      input$counter.settings,
      input$light.setting,
      input$blur,
      input$remove.lines,
      input$cut.well.edges,
      input$insert.value.for.pixels.outside.well
    )
    if (is.null(input$choose_dir_save_settings)) {
      output$print_print_settings <-
        renderText("You need to select a directory for where to save your settings file.")
    }
    
    if (!is.null(input$choose_dir_save_settings)) {
      output$print_print_settings <-
        renderText({
          paste(
            parseDirPath(volumes, input$choose_dir_save_settings),
            "/",
            input$unique.settings.name,
            ".csv",
            sep = ""
          )
        })
      
      write.csv(
        print.settings,
        file = paste(
          parseDirPath(volumes, input$choose_dir_save_settings),
          "/",
          input$unique.settings.name,
          ".csv",
          sep = ""
        ),
        quote = T
      )
    }
    
  })
  
  #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
  # For progress bar: so better reflects actual counting of images
  #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
  
  improg <- 0
  
  observeEvent(input$submit, {
    #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
    # Begin Viridot plaque counting
    #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
    for (j in 1:length(input$select_plates_plaque_counter)) {
      image.to.display.wellname <- ""
      number.of.plaques <- ""
      
      
      if (is.null(input$set_root_directory))  {
        output$print_warnings <-
          renderText(
            "You need to select a directory using the button '(1) Select the directory that contains your plate folders'."
          )
        next
      }
      if (is.null(input$select_plates_plaque_counter))  {
        output$print_warnings <-
          renderText(
            "You need to select a plate/plates using the '(2) Select the plate folders that contain well images to count' box."
          )
        next
      }
      
      if (is.null(input$wells))  {
        output$print_warnings <-
          renderText("You need to select a well image using the '(3) Select the well images to count' box.")
        next
      }
      
      if (is.null(input$save_output_files_dir) &
          (
            input$button.save.tables == "Yes" |
            input$print.image == "Yes" | input$plaque.sizes == "Yes"
          )) {
        output$print_warnings <-
          renderText(
            "You have said that you want to print out plaque tables, save plaque-circled images or count plaque sizes, but have not specified a directory for where to print those tables/images.  Select an output directory using the 'Directory for saving plaque tables/images' button."
          )
        next
      }
      
      output$print_warnings <- renderText("")
      
      #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
      # Tell Viridot where to look for the images
      #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
      ret.dir <-
        paste(
          parseDirPath(volumes, input$set_root_directory),
          "/",
          input$select_plates_plaque_counter[j],
          sep = ""
        )
      
      if (length(list.files(ret.dir)) == 0) {
        output$print_warnings <-
          renderText(
            "You have either selected a file (rather than a folder) in the box under the title '(2) Select the plate folders that contain well images to count', or the plate folder you selected has nothing in it."
          )
        next
        
      }
      
      if (input$wells == "all")
        wells.to.analyze <- mixedsort(list.files(ret.dir))
      
      if (input$wells != "all")
        wells.to.analyze <- input$wells
      
      directions.to.wells.to.analyze <-
        paste(ret.dir, "/", wells.to.analyze, sep = "")
      
      
      if (input$plate.96.wells == "Not a plate") {
        plaquecount.cursize <- rep(NA, times = length(wells.to.analyze))
        names(plaquecount.cursize) <- wells.to.analyze
      }
      
      if (input$plate.96.wells == "96 well plate") {
        well.names.no.ext <-
          toupper(sub("^([^.]*).*", "\\1", wells.to.analyze))
        absolute.plate.wells <-
          matrix(paste(paste(
            rep(LETTERS[1:8], each = 12), 1:12, sep = ""
          ), sep = ""), 8, 12, byrow = T)
        plaquecount.cursize <- matrix(NA, 8, 12, byrow = T)
        rownames(plaquecount.cursize) <- LETTERS[1:8]
        colnames(plaquecount.cursize) <- 1:12
      }
      
      
      if (input$plate.96.wells == "24 well plate") {
        well.names.no.ext <-
          toupper(sub("^([^.]*).*", "\\1", wells.to.analyze))
        absolute.plate.wells <-
          matrix(paste(paste(rep(
            LETTERS[1:4], each = 6
          ), 1:6, sep = ""), sep = ""), 4, 6, byrow = T)
        plaquecount.cursize <- matrix(NA, 4, 6, byrow = T)
        rownames(plaquecount.cursize) <- LETTERS[1:4]
        colnames(plaquecount.cursize) <- 1:6
      }
      
      
      withProgress(
        message = 'Counting plaques',
        value = improg / (
          length(input$select_plates_plaque_counter) * length(input$wells)
        ),
        min = 0,
        max = 1,
        
        {
          for (i in 1:length(wells.to.analyze)) {
            steps.written <- c(
              "Raw well image",
              "Step 1. Select light setting",
              "Step 2. Blur image",
              "Extra option: Remove strings/fibers in image",
              "Step 3. cut well edges and insert value for pixels outside well",
              "Step 4. Apply contrast to image based on background and plaque intensity",
              "Step 5. Select difference in pixel value to distinguish plaque from
              background and window for applying the thresholding algorithm to the image",
              "Step 6. Dilate your plaques to ensure they are counted as single plaques",
              "Step 7. Cut overlapping plaques so they are counted separately",
              "Step 8. Define the minimum and maximum pixel sizes to count as plaques",
              "Final counted plaques with well radius shown"
            )
            
            
            # Each time through the loop, add another row of data. This is
            # a stand-in for a long-running computation.
            improg <- 1 + improg
            # Increment the progress bar, and update the detail text.
            incProgress(improg / (
              length(input$select_plates_plaque_counter) * length(input$wells.to.analyze)
            ),
            detail = paste("Counting image", improg))
            
            
            
            if (file_ext(directions.to.wells.to.analyze[i]) == "")
              next
            
            if (file_ext(directions.to.wells.to.analyze[i]) == "CTL")
              error1 <-
              try(readImage(directions.to.wells.to.analyze[i], type = "tiff"),
                  silent = T)
            else
              error1 <-
              try(readImage(directions.to.wells.to.analyze[i]),
                  silent = T)
            
            if (class(error1) == "try-error")
              next
            
            if (file_ext(directions.to.wells.to.analyze[i]) == "CTL")
              im <-
              readImage(directions.to.wells.to.analyze[i], type = "tiff")
            else
              im <-  readImage(directions.to.wells.to.analyze[i])
            
            
            if (dim(im)[1] != dim(im)[2]) {
              output$print_warnings <-
                renderText(
                  "Well images need to be squares (width and height are the same number of pixels)."
                )
              next
            }
            
            
            if (length(dim(im))<3) satim <- im
            if (length(dim(im))>=3) {
              
            if (input$light.setting == "hue")
              satim <- convert.saturation(im, print = "hue")
            if (input$light.setting == "value")
              satim <- convert.saturation(im, print = "value")
            if (input$light.setting == "saturation")
              satim <- convert.saturation(im)
            if (input$light.setting == "red")
              satim <- imageData(im)[, , 1]
            if (input$light.setting == "green")
              satim <- imageData(im)[, , 2]
            if (input$light.setting == "blue")
              satim <- imageData(im)[, , 3]
            if (input$light.setting == "1-saturation")
              satim <- 1 - convert.saturation(im)
            if (input$light.setting == "1-red")
              satim <- 1 - imageData(im)[, , 1]
            if (input$light.setting == "1-green")
              satim <- 1 - imageData(im)[, , 2]
            if (input$light.setting == "1-blue")
              satim <- 1 - imageData(im)[, , 3]
            
            if (input$light.setting == "red and green only") {
              satim <- im
              imageData(satim)[, , 2] <- NA
              satim <-
                (imageData(satim)[, , 1] + imageData(satim)[, , 3]) /
                2
            }
            if (input$light.setting == "red and blue only") {
              satim <- im
              imageData(satim)[, , 3] <- NA
              satim <-
                (imageData(satim)[, , 1] + imageData(satim)[, , 2]) /
                2
            }
            if (input$light.setting == "blue and green only") {
              satim <- im
              imageData(satim)[, , 1] <- NA
              satim <-
                (imageData(satim)[, , 2] + imageData(satim)[, , 3]) /
                2
            }
            if (input$light.setting == "1-(red and green only)") {
              satim <- im
              imageData(satim)[, , 2] <- NA
              satim <-
                1 - ((imageData(satim)[, , 1] + imageData(satim)[, , 3]) / 2)
            }
            if (input$light.setting == "1-(red and blue only)") {
              satim <- im
              imageData(satim)[, , 3] <- NA
              satim <-
                1 - ((imageData(satim)[, , 1] + imageData(satim)[, , 2]) / 2)
            }
            if (input$light.setting == "1-(blue and green only)") {
              satim <- im
              imageData(satim)[, , 1] <- NA
              satim <-
                1 - ((imageData(satim)[, , 2] + imageData(satim)[, , 3]) / 2)
            }
            
            } 
            
            
            nowhitespace <-
              cir.cut(satim,
                      radiuscut = input$cut.well.edges,
                      insert.value = NA)
            
            
            bgpeak <-
              round(density(nowhitespace, na.rm = T, bw = .01)$x[which.max(density(nowhitespace, na.rm = T, bw = .01)$y)], digits = 2)
            if (bgpeak >  0.2)
              bg <- .5
            if (bgpeak >  0.15)
              bg <- .75
            if (bgpeak >  0.06)
              bg <- 1
            if (bgpeak >= .02  &
                bgpeak <= .06)
              bg <-
              seq(1, 3, .5)[which(rev(seq(.02, .06, .01)) == bgpeak)]
            if (bgpeak < .02)
              bg <- 3
            
            gr5 <-    length(nowhitespace[nowhitespace > .5])
            gr8 <-    length(nowhitespace[nowhitespace > .8])
            if (gr5 == 0)
              gr5 <- 1
            if (gr8 / gr5 < 0.1144)
              si <- 2
            if (0.1144 <= gr8 / gr5)
              si <- 3
            
            thisoff <- .08
            # for small plaques
            if (si == 2)
              thisoff <- .12
            # for small/light plaques
            if (gr8 / gr5 < 0.06)
              thisoff <- .10
            
            
            if (input$counter.settings %in% c("Auto")) {
              blur <- si
              contrast.plaque <- si
              contrast.background <- bg
              threshold.difference <- thisoff
              insert.value.for.pixels.outside.well <- "Black (0)"
              # for really dark background
              if (bgpeak > .6)
                insert.value.for.pixels.outside.well <-
                "Median background pixel value"
              
              updateSliderInput(session, inputId = 'blur', value = blur)
              updateSliderInput(session, inputId = 'contrast.plaque', value = contrast.plaque)
              updateSliderInput(session, inputId = 'contrast.background', value = contrast.background)
              updateSliderInput(session, inputId = 'threshold.difference', value = threshold.difference)
              updateSelectInput(session, inputId = 'insert.value.for.pixels.outside.well', selected = insert.value.for.pixels.outside.well)
              
              
            }
            print(input$counter.settings)
            if (input$counter.settings %in% c("User-specified")) {
              blur <- input$blur
              contrast.plaque <- input$contrast.plaque
              contrast.background <- input$contrast.background
              threshold.difference <- input$threshold.difference
              insert.value.for.pixels.outside.well <-
                input$insert.value.for.pixels.outside.well
            }
            
            
            
            if (blur > 0)
              bim1 <- gblur(satim, sigma = blur)
            if (blur == 0)
              bim1 <- satim
            
            
            bim <- bim1
            
            if (input$show.what.is.done.at.each.step == steps.written[3]) {
              image.to.display <- bim1
            }
            rm(bim1)
            
            if (input$remove.lines > (0)) {
              bim2 <-
                input$remove.lines + satim - (1 - gblur(convert.saturation(im, print =
                                                                             "hue"), sigma = 2))
              bim <- bim2
              
              rm(bim2)
            }
            
            
            if (input$show.what.is.done.at.each.step == steps.written[1]) {
              image.to.display <- im
            }
            
            if (input$show.what.is.done.at.each.step == steps.written[2]) {
              image.to.display <- satim
            }
            rm(satim)
            
            
            
            if (insert.value.for.pixels.outside.well == "Black (0)")
              cutim <- cir.cut(bim,
                               radiuscut = input$cut.well.edges,
                               insert.value = 0)
            if (insert.value.for.pixels.outside.well == "Median background pixel value")
              cutim <-
              cir.cut(bim,
                      radiuscut = input$cut.well.edges,
                      insert.value = bgpeak)
            
            
            if (input$show.what.is.done.at.each.step == steps.written[4]) {
              image.to.display <- bim
            }
            rm(bim)
            
            contim <-
              contrast.background * (cutim ^ contrast.plaque)
            
            if (input$show.what.is.done.at.each.step == steps.written[5]) {
              image.to.display <- cutim
            }
            rm(cutim)
            
            threshim <-
              thresh(
                contim,
                w = input$theshold.window.size,
                h = input$theshold.window.size,
                threshold.difference
              )
            
            if (input$show.what.is.done.at.each.step == steps.written[6]) {
              image.to.display <- contim
            }
            rm(contim)
            
            if (input$dilation.size > 0)
              dilim <-
              dilate(threshim,
                     makeBrush(input$dilation.size, shape = "disc"))
            if (input$dilation.size == F)
              dilim <- threshim
            dilim <- fillHull(dilim)
            distim <- distmap(dilim)
            rm(dilim)
            
            if (input$show.what.is.done.at.each.step == steps.written[7]) {
              image.to.display <- threshim
            }
            rm(threshim)
            
            
            waterim <-
              watershed(distim, tolerance = input$plaque.overlap.tolerance)
            
            
            if (input$show.what.is.done.at.each.step == steps.written[8]) {
              image.to.display <- distim
            }
            rm(distim)
            
            nowhitespace[!is.na(nowhitespace)] <- 1
            nowhitespace[is.na(nowhitespace)] <- 0
            
            minmaxim <- waterim
            
            if (input$show.what.is.done.at.each.step == steps.written[9]) {
              image.to.display <- normalize(waterim)
            }
            rm(waterim)
            
            count.all.objects <- table(minmaxim)
            
            number.of.plaques <-
              length(count.all.objects[count.all.objects > input$min.pixel.counted &
                                         count.all.objects < input$max.pixel.counted])
            
            
            
            if (is.null(computeFeatures.shape(minmaxim))) {
              all.plaque.table <- "no plaques"
            }
            
            if (!is.null(computeFeatures.shape(minmaxim))) {
              all.plaque.table <- as.matrix(computeFeatures.shape(minmaxim))
              minmaxim[minmaxim %in% which(
                all.plaque.table[, "s.area"] < input$min.pixel.counted |
                  all.plaque.table[, "s.area"] > input$max.pixel.counted
              )] <- 0
            }
            
            plaque.outlines <-
              paintObjects(minmaxim,
                           im,
                           col = input$plaque.outline.color,
                           thick = T)
            plaque.outlines.well.outlines <-
              paintObjects(
                nowhitespace,
                plaque.outlines,
                col = input$well.outline.color,
                thick = T
              )
            
            
            
            if (input$show.what.is.done.at.each.step == steps.written[10]) {
              image.to.display <- normalize(minmaxim)
            }
            rm(minmaxim)
            
            
            if (input$show.what.is.done.at.each.step == steps.written[11]) {
              image.to.display <- plaque.outlines.well.outlines
              image.to.display.wellname <- wells.to.analyze[i]
            }
            
            #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
            # For progress bar: so better reflects actual counting of images
            #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
            if (length(input$save_output_files_dir) > 0) {
              save.outlined.wells <-
                parseDirPath(volumes, input$save_output_files_dir)
              dir.create(
                paste(
                  save.outlined.wells,
                  "/",
                  input$select_plates_plaque_counter[j],
                  sep = ""
                )
              )
            }
            
            if (input$print.image == "Yes")  {
              image.file.name <-
                paste(
                  paste(
                    save.outlined.wells,
                    "/",
                    input$select_plates_plaque_counter[j],
                    sep = ""
                  )
                  ,
                  "/",
                  sub("^([^.]*).*", "\\1", wells.to.analyze[i]),
                  "-",
                  input$user_names_output_files,
                  ".png",
                  sep = ""
                )
              writeImage(image.to.display, image.file.name)
            }
            
            if (input$plaque.sizes == "Yes") {
              plaque.file.name <-
                paste(
                  paste(
                    save.outlined.wells,
                    "/",
                    input$select_plates_plaque_counter[j],
                    sep = ""
                  )
                  ,
                  "/",
                  sub("^([^.]*).*", "\\1", wells.to.analyze[i]),
                  "-",
                  input$user_names_output_files,
                  "-plaque-sizes.xls",
                  sep = ""
                )
              write.table(
                all.plaque.table,
                plaque.file.name,
                sep = "\t",
                col.names = NA
              )
            }
            
            
            if (input$plate.96.wells == "Not a plate")
              plaquecount.cursize[i] <- number.of.plaques
            if (input$plate.96.wells == "96 well plate" |
                input$plate.96.wells == "24 well plate")
              plaquecount.cursize[which(absolute.plate.wells %in% well.names.no.ext[i],
                                        arr.ind = T)] <-
              number.of.plaques
            
            
            unique.plate.name <-
              strsplit(ret.dir, "/")[[1]][length(strsplit(ret.dir, "/")[[1]])]
            
            if (input$button.save.tables == "Yes")  {
              save.tables <- parseDirPath(volumes, input$save_output_files_dir)
              write.table(
                plaquecount.cursize,
                paste(
                  save.tables,
                  "/",
                  input$user_names_output_files,
                  "-",
                  input$select_plates_plaque_counter[j],
                  "-plaque-counts-in-table.xls",
                  sep = ""
                ),
                sep = "\t",
                col.names = NA
              )
            }
            
            
            
          }
          
        }
      )
      
      
      output$image.to.display <- renderImage({
        image.file.name <- tempfile(fileext = ".png")
        writeImage(image.to.display,  image.file.name)
        
        list(src = image.file.name,
             contentType = "image/png",
             alt = "This text appears when something is wrong?")
        
      }, deleteFile = TRUE)
      
      output$wellname <-  renderText({
        image.to.display.wellname
      })
      
      output$plaquecount <-  renderText({
        number.of.plaques
      })
      
    }
    
    
    
    
  })
  
  
  
  #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
  # NEUTRALIZATION TITER ESTIMATION
  #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
  
  shinyDirChoose(
    input,
    'set_neut_output_directory',
    roots = volumes,
    session = session,
    restrictions = system.file(package = 'base')
  )
  
  
  observeEvent(input$set_neut_output_directory, {
    output$print_set_neut_output_directory <-
      renderPrint({
        parseDirPath(volumes, input$set_neut_output_directory)
      })
  })
  
  
  shinyDirChoose(
    input,
    'set_neut_root_directory',
    roots = volumes,
    session = session,
    restrictions = system.file(package = 'base')
  )
  
  observeEvent(input$set_neut_root_directory, {
    updateSelectInput(session,
                      inputId = 'load_plaque_count_table',
                      choices = c(mixedsort(list.files(
                        paste(
                          parseDirPath(volumes, input$set_neut_root_directory),
                          sep = ""
                        )
                      ))))
    
  })
  
  
  
  observeEvent(input$load_plaque_count_table, {
    #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
    neut.dir <-
      paste(
        parseDirPath(volumes, input$set_neut_root_directory),
        "/",
        input$load_plaque_count_table,
        sep = ""
      )
    
    
    
    err.rt <-
      try(as.matrix(read.table(
        file = neut.dir[1],
        sep = "\t",
        header = T,
        row.names = 1
      )))
    
    output$print_warnings_neut <- renderText("")
    
    if (class(err.rt) == "try-error") {
      output$print_warnings_neut <-
        renderText(
          "You have tried to import a file that is not formatted correctly or is not a table of plaque data."
        )
      if (length(grep("duplicate 'row.names' are not allowed", err.rt)) >
          0)
        output$print_warnings_neut <-
          renderText(
            "You have tried to import a file that has duplicated rownames.  Please correct your table so that you have unique row names and try to read in again."
          )
      
      
    }
    
    if (class(err.rt) != "try-error") {
      plaque.count.table <-
        as.matrix(read.table(
          file = neut.dir[1],
          sep = "\t",
          header = T,
          row.names = 1
        ))
      
      output$plaque_table <-
        renderTable(cbind(plaque.count.table),
                    rownames = T,
                    colnames = T)
      
    }
    
    
  })
  
  
  shinyFileChoose(
    input,
    'load_plate_template',
    roots = volumes,
    session = session,
    restrictions = system.file(package = 'base')
  )
  
  
  
  observeEvent(input$load_plate_template, {
    err.rt3 <-
      try(read.csv(
        file = as.character(
          parseFilePaths(volumes, input$load_plate_template)$datapath[1]
        ),
        header = T,
        row.names = 1
      ))
    
    output$print_warnings_neut <- renderText("")
    
    if (class(err.rt3) == "try-error") {
      output$print_warnings_neut <-
        renderText("You have tried to import a file that is not formatted correctly or is not template.")
    }
    
    
    if (class(err.rt3) != "try-error") {
      template.table <-
        as.matrix(read.csv(
          file =  as.character(
            parseFilePaths(volumes, input$load_plate_template)$datapath[1]
          ),
          header = T,
          row.names = 1
        ))
      
      output$template_table <-
        renderTable(template.table,
                    rownames = T,
                    colnames = T)
      
      
    }
    
    
  })
  
  
  observeEvent(input$print_dilutions, {
    dilutions <-
      input$dilution.series ^ c(
        log(input$starting.dilution, base = input$dilution.series):log(input$total.dilutions, base =
                                                                         input$dilution.series)
      )
    
    output$table_dilutions <-
      renderTable(
        cbind(1:length(dilutions), dilutions),
        colnames = FALSE,
        digits = input$sig.figs.dil
      )
    
  })
  
  observeEvent(input$estimate_neut_curve, {
    output$print_warnings_neut <- renderText("")
    
    if (is.null(input$load_plaque_count_table))  {
      output$print_warnings_neut <-
        renderText("You need to upload a plaque count table using the 'load plaque count table' button.")
      
    }
    
    req(input$load_plaque_count_table)
    
    if (!is.null(input$load_plate_template)) {
      err.rt3 <-
        try(read.csv(
          file = as.character(
            parseFilePaths(volumes, input$load_plate_template)$datapath[1]
          ),
          header = T,
          row.names = 1
        ))
      
      
      if (class(err.rt3) != "try-error") {
        template.table <-
          as.matrix(read.csv(
            file =  as.character(
              parseFilePaths(volumes, input$load_plate_template)$datapath[1]
            ),
            header = T,
            row.names = 1
          ))
        
        output$template_table <-
          renderTable(template.table,
                      rownames = T,
                      colnames = T)
        
        
      }
      
    }
    if (is.null(input$load_plate_template)) {
      template.table <-
        matrix(paste(rep(paste("S", 4:1, sep = ""), each = 24), "-", rep(paste(
          "D", 1:12, sep = ""
        ), times = 8), sep = ""), 8, 12, byrow = T)
      rownames(template.table) <- LETTERS[1:8]
      colnames(template.table) <- paste("X", 1:12, sep = "")
      
      
      output$template_table <-
        renderTable(template.table,
                    rownames = T,
                    colnames = T)
    }
    
    
    vircol <- input$vircol
    vircoldark <- input$vircoldark
    experiment.name <- input$experiment.name
    
    
    dilutions <-
      input$dilution.series ^ c(
        log(input$starting.dilution, base = input$dilution.series):log(input$total.dilutions, base =
                                                                         input$dilution.series)
      )
    
    
    
    low.titer.limit <- input$low.titer.limit
    upper.titer.limit <- input$upper.titer.limit
    
    options(show.error.messages = FALSE)
    
    
    
    
    
    #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
    # read in data
    #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
    neut.dir <-
      paste(
        parseDirPath(volumes, input$set_neut_root_directory),
        "/",
        input$load_plaque_count_table,
        sep = ""
      )
    
    plaquetables.names.no.ext <-
      (sub("^([^.]*).*", "\\1", input$load_plaque_count_table))
    
    
    for (j in 1:length(neut.dir)) {
      err.rt <-
        try(as.matrix(read.table(
          file = neut.dir[j],
          sep = "\t",
          header = T,
          row.names = 1
        )))
      
      if (class(err.rt) == "try-error") {
        output$print_warnings_neut <-
          renderText(
            "You have tried to import a file that is not formatted correctly or is not a table of plaque data."
          )
        next
      }
      
      if (class(err.rt) != "try-error") {
        plaque.count.table <-
          as.matrix(read.table(
            file = neut.dir[j],
            sep = "\t",
            header = T,
            row.names = 1
          ))
        
        output$plaque_table <-
          renderTable(cbind(plaque.count.table),
                      rownames = T,
                      colnames = T)
        
      }
      
      if (dim(plaque.count.table)[1] != dim(template.table)[1] |
          dim(plaque.count.table)[2] != dim(template.table)[2]) {
        output$print_warnings_neut <-
          renderText(
            "Your template table does not match the dimensions of the plate you are trying to import."
          )
        next
      }
      
      
      # Set back to NA for each titration
      NAwells <- NA
      
      # corresponds to samples 1, 2, 3, and 4
      unique.template.identities <-
        mixedsort(unique(unlist(strsplit(
          template.table, "-"
        ))))
      S.templ.id <-
        unique.template.identities[grep("S", unique.template.identities)]
      C.templ.id <-
        unique.template.identities[grep("C", unique.template.identities)]
      
      
      if (input$only.run.sample.subset != "") {
        comma.subset.samp <- grep(",", input$only.run.sample.subset)
        if (length(comma.subset.samp) > 0)
          S.templ.id <-
            mixedsort(unique(unlist(
              strsplit(input$only.run.sample.subset, ",")
            )))
        if (length(comma.subset.samp) == 0)
          S.templ.id <- input$only.run.sample.subset
      }
      
      
      serum.names <- rep(NA, times = length(S.templ.id))
      serum.names.written <-
        unlist(strsplit(input$serum.names, split = ","))
      if (length(serum.names.written) != length(serum.names)) {
        output$print_warnings_neut <-
          renderText(
            "The number of serum names given doesn't match the number of samples in your template. Please correct this."
          )
        next
      }
      serum.names[1:length(serum.names.written)] <-
        serum.names.written
      
      virus.names <- rep(NA, times = length(S.templ.id))
      virus.names.written <-
        unlist(strsplit(input$virus.names, split = ","))
      if (length(virus.names.written) != length(serum.names.written))
        virus.names <-
        rep(virus.names.written[1], times = length(serum.names))
      if (length(virus.names.written) == length(serum.names.written))
        virus.names <- virus.names.written
      
      # set up data table for printing titers
      table.of.titers <- matrix(NA, 6, length(S.templ.id))
      colnames(table.of.titers) <-
        paste("sample", 1:length(S.templ.id))
      rownames(table.of.titers) <-
        c(
          "serum.name",
          "virus.name",
          "neut.titer",
          "lower 95% ci neut. titer",
          "upper 95% ci neut. titer",
          "slope"
        )
      table.of.titers[1,] <- paste(serum.names, sep = "")
      table.of.titers[2,] <- paste(virus.names, sep = "")
      
      
      output$print_control_means <- renderText("")
      if (length(C.templ.id) > 0) {
        control.means <- matrix(NA, length(C.templ.id), 8)
        colnames(control.means) <-
          c("Control",
            "Min.",
            "1st Qu.",
            "Median",
            "Mean",
            "3rd Qu.",
            "Max.",
            "NA's")
        
        for (cm in 1:length(C.templ.id)) {
          plaque.control.data <-
            plaque.count.table[which(template.table %in% C.templ.id[cm])]
          
          if (length(which(!is.na(plaque.control.data))) > 2 &
              length(which(is.na(plaque.control.data))) > 0)
            control.means[cm, 1:8] <-
              c(C.templ.id[cm], summary(plaque.control.data))
          if (length(which(!is.na(plaque.control.data))) > 2 &
              length(which(is.na(plaque.control.data))) == 0)
            control.means[cm, 1:7] <-
              c(C.templ.id[cm], summary(plaque.control.data))
          
        }
        
        
        output$print_control_means <- renderTable(control.means)
        
      }
      image.file.temp.name <- tempfile(fileext = ".png")
      
      png(
        image.file.temp.name,
        width = 400 * length(S.templ.id),
        height = 500
      )
      layout(mat = matrix(c(1:length(S.templ.id)), 1, length(S.templ.id), byrow =
                            T))
      par(mar = c(8, 7, 3, 0))
      par(bg = "white", fg = "white")
      
      
      for (i in 1:length(S.templ.id)) {
        unique.sample.identities <-
          mixedsort(unique(template.table[grep(S.templ.id[i], template.table)]))
        D.templ.id <-
          unique.sample.identities[grep("D", unique.sample.identities)]
        
        plaques.per.sample <- c()
        
        failhere <- FALSE
        
        for (d in 1:length(D.templ.id)) {
          # plaque counts for that titration
          
          next.column.plaques.per.sample <-
            plaque.count.table[which(template.table %in% paste(D.templ.id[d], sep =
                                                                 ""))]
          
          plaques.per.sample <-
            rbind(plaques.per.sample,
                  next.column.plaques.per.sample)
          
          if (d > 1 &
              length(next.column.plaques.per.sample) != length(plaques.per.sample[d -
                                                                                  1,])) {
            failhere <- TRUE
            output$print_warnings_neut <-
              renderText(
                "The number of repeats you have per dilution is not the same across the sample in your template.  Please correct this."
              )
            next
          }
          
        }
        
        if (failhere == TRUE)
          
          next
        
        if (length(dilutions) < dim(plaques.per.sample)[1]) {
          output$print_warnings_neut <-
            renderText(
              "The number of dilutions you have in your dilution series is less than the number of dilutions you have indicated in your template.  Please correct this."
            )
          next
        }
        
        dilutions <- (dilutions[1:dim(plaques.per.sample)[1]])
        
        
        plaque.counts <- c(plaques.per.sample)
        
        # dilutions for that titration
        logdil <-
          rep((dilutions), times = dim(plaques.per.sample)[2])
        
        if (logdil[length(logdil)] > logdil[1])
          set.of.dil.values <-
          10 ^ ((seq(
            log10(logdil[1]) - 2, log10(logdil)[length(logdil)] + 2, 0.01
          )))
        
        if (logdil[length(logdil)] < logdil[1])
          set.of.dil.values <-
          10 ^ (rev(seq(
            log10(logdil)[length(logdil)] - 2, log10(logdil[1]) + 2, 0.01
          )))
        
        M <- NULL
        
        
        if (input$control.well.count == "eachtop") {

            test.M <-
            try(drm((plaque.counts/M) ~ logdil,
                    fct = LL.4(fixed = c(NA, bottom.asympt, NA, NA)),
                    na.action = na.omit
            ),
            silent = T)
            
          if (class(test.M) != "try-error") {
            test.M.curve <-
              drm((plaque.counts) ~ logdil,
                  fct = LL.4(fixed = c(NA, bottom.asympt, NA, NA)),
                  na.action = na.omit
              )
            M <- unname(test.M.curve$coefficients["d:(Intercept)"])
          }
          if (class(test.M) == "try-error") {
            M <- median(tail(plaques.per.sample, 3), na.rm = T)
            
          }
          if (exists("M") == FALSE | is.null(M)) {
            M <- median(tail(plaques.per.sample, 3), na.rm = T)
            
          }
        }
        
        
        if (length(grep(paste(c("D", "C"), collapse = "|"), input$control.well.count)) >
            0) {
          comma <- grep(",", input$control.well.count)
          if (length(comma) > 0)
            these.controls <-
              mixedsort(unique(unlist(
                strsplit(as.character(input$control.well.count), split = ",")
              )))
          if (length(comma) == 0)
            these.controls <- input$control.well.count
          find.control.match <-
            grep(paste(these.controls, collapse = "|"),
                 template.table)
          if (length(find.control.match) > 0)
            M <-
            median(unlist(plaque.count.table[find.control.match]), na.rm = T)
          else   {
            output$print_warnings_neut <-
              renderText(
                "You have tried to set a control value that is not in your template. Please include control instructions that match your template"
              )
            next
          }
        }
        
        
        convert.control.well.count.number <-
          as.numeric(input$control.well.count)
        if (is.numeric(convert.control.well.count.number) &
            !is.na(convert.control.well.count.number)) {
          M <- convert.control.well.count.number
        }
        
        

        
        if (input$plot_duplicates == "polygon") {
          top <- c()
          bottom <- c()
          
          for (k in 1:length(dilutions)) {
            paired <- c(plaques.per.sample[k,])
            top[k] <- max(paired)
            bottom[k] <- min(paired)
          }
          top <- top / M
          bottom <- bottom / M
          
          # Make polygon for the figure
          SDhigh <- top
          SDlow <- top - (top - bottom)
          x.sd.poly <-
            c(log(dilutions)[1:length(dilutions)], log(dilutions)[length(dilutions):1])
          y.sd.poly <-
            c(SDlow[1:length(dilutions)], SDhigh[length(dilutions):1])
          
          
          plot(
            log(dilutions),
            # doesn't matter: just plotting null plot, 2 is OK:
            c(top + bottom) / 2,
            ylim = c(0, 1.5),
            xlim = range(log(dilutions)),
            axes = F,
            xlab = "",
            ylab = "",
            type = "n"
          )
          
          if (length(which(is.na(y.sd.poly))) > 0) {
            NAwells <- which(is.na(y.sd.poly))
            TFNAwells <- is.na(y.sd.poly)
            x.sd.poly <- x.sd.poly[-NAwells]
            y.sd.poly <- y.sd.poly[-NAwells]
            
          }
          
          polygon(x.sd.poly,
                  y.sd.poly,
                  col = "grey95",
                  border = "grey60")
          
        }
        if (input$plot_duplicates == "error bars (standard deviation)") {
          sd.dup <- c()
          mean.dup <- c()
          
          for (k in 1:length(dilutions)) {
            mean.dup[k] <-
              mean(c(plaques.per.sample[k,] / M),
                   na.rm = T)
            sd.dup[k] <-
              sd(c(plaques.per.sample[k,] / M),
                 na.rm = T)
          }
          
          
          errbar(
            log(dilutions),
            mean.dup,
            mean.dup + sd.dup,
            mean.dup - sd.dup,
            ylim = c(0, 1.5),
            xlim = range(log(dilutions)),
            axes = F,
            xlab = "",
            ylab = "",
            col = "grey40",
            lwd = 2,
            errbar.col = "grey40",
            pch = 16
          )
          
        }
        
        
        title(ylab = "Percent neutralization                   ",
              line = 5,
              cex.lab = 2)
        title(
          xlab = input$xlabelneut,
          line = 6.5,
          cex.lab = 2
        )
        
        axis(
          1,
          log(dilutions),
          round((dilutions), digits = input$sig.figs.dil),
          col.ticks = "grey",
          cex.axis = 2,
          las = 2
        )
        axis(
          2,
          c(0, .5, 1),
          c("100%", "50%", "0%"),
          col.ticks = "grey",
          cex.axis = 2,
          las = 2
        )
        
        
        box("figure", col = "grey", lwd = 0.5)
        
        dil.values <-
          c(9., .8, .7, .6, .5, .4, .3, .2, .1)[which(paste("PRNT", (seq(10, 90, 10)), sep =
                                                              "") %in% input$PRNTcut)]
        abline(h = dil.values,
               col = "grey",
               lwd = 3)
        
        
        title(
          main = paste("SR=", serum.names[i], "\nAG=", virus.names[i]),
          line = -2,
          cex.main = 1.5
        )
        
        if (input$resistant_fraction == "FALSE")
          bottom.asympt <- 0
        if (input$resistant_fraction == "TRUE")
          bottom.asympt <- NA
        
        
        if (input$NIH.titer.estimation == FALSE) {
          test <-
            try(drm((plaque.counts / M) ~ logdil,
                    fct = LL.4(fixed = c(NA, bottom.asympt, 1, NA)),
                    na.action = na.omit
            ),
            silent = T)
          
          
          if (class(test) == "try-error") {
            next
          }
          
          if (class(test) != "try-error") {
            logisticurve <-
              drm((plaque.counts / M) ~ logdil,
                  fct = LL.4(fixed = c(NA, bottom.asympt, 1, NA)),
                  na.action = na.omit
              )
            
            suppressWarnings(predicted.titercut <-
                               (
                                 predict(
                                   logisticurve,
                                   interval = "confidence",
                                   data.frame(x = set.of.dil.values)
                                 )
                               ))
            
            
            reduction.at.value <-
              which(round(predicted.titercut[, "Prediction"], digits = 1) == dil.values)
            

            if (length(reduction.at.value) < 1 
                |sign(logdil[1] - logdil[length(logdil)]) != unname(sign(logisticurve$coefficients["b:(Intercept)"]))
                ) {
              if (sign(logdil[1] - logdil[length(logdil)]) == -1)
                titer <- paste("<",
                               min(c(
                                 low.titer.limit, upper.titer.limit
                               ), na.rm = T),
                               sep = "")
              if (sign(logdil[1] - logdil[length(logdil)]) == 1)
                titer <- paste(">",
                               max(c(
                                 low.titer.limit, upper.titer.limit
                               ), na.rm = T),
                               sep = "")
              table.of.titers[3, i] <- titer
              
            }
            
            
            
            
            else {
              titer <- round(
                estimate.x.dose.value(
                  predicted.v = predicted.titercut[, "Prediction"],
                  dilvalues.v = dil.values,
                  set.of.dil.values.v = set.of.dil.values
                ),
                digits =
                  input$sig.figs.dil
              )
              
              if (is.numeric(titer) &
                  titer < min(low.titer.limit, upper.titer.limit, na.rm = T))
                titer[titer < min(low.titer.limit, upper.titer.limit, na.rm = T)] <-
                  paste("<",
                        min(low.titer.limit, upper.titer.limit, na.rm = T),
                        sep = "")
              
              if (is.numeric(titer) &
                  titer > max(low.titer.limit, upper.titer.limit, na.rm = T))
                titer[titer > max(low.titer.limit, upper.titer.limit, na.rm = T)] <-
                  paste(">",
                        max(low.titer.limit, upper.titer.limit, na.rm = T),
                        sep = "")
              
              
              table.of.titers[3, i] <- titer
              
              reduction.at.value.low <-
                which(round(predicted.titercut[, "Lower"], digits = 1) == dil.values)
              
              
              if (length(reduction.at.value.low) > 0)
                table.of.titers[5, i] <-
                round(
                  estimate.x.dose.value(
                    predicted.v = predicted.titercut[, "Lower"],
                    dilvalues.v =
                      dil.values,
                    set.of.dil.values.v =
                      set.of.dil.values
                  ),
                  digits =
                    input$sig.figs.dil
                )
              
              if (length(reduction.at.value.low) == 0) {
                table.of.titers[5, i] <-   "ci too large"
              }
              
              #          print("pass3")
              
              reduction.at.value.upper <-
                which(round(predicted.titercut[, "Upper"], digits = 1) == dil.values)
              
              
              if (length(reduction.at.value.upper) > 0)
                table.of.titers[4, i] <-
                round(
                  estimate.x.dose.value(
                    predicted.v = predicted.titercut[, "Upper"],
                    dilvalues.v = dil.values,
                    set.of.dil.values.v = set.of.dil.values
                  ),
                  digits =
                    input$sig.figs.dil
                )
              
              
              
              if (length(reduction.at.value.upper) == 0) {
                table.of.titers[4, i] <-  "ci too large"
              }
              
              
              table.of.titers[6, i] <-
                round(logisticurve$coefficients["b:(Intercept)"],
                      digits = input$sig.figs.dil + 2)
              
              logist.LOW.ci <- predicted.titercut[, "Lower"]
              logist.HIGH.ci <- predicted.titercut[, "Upper"]
              
              
              
              x.logis.poly <-
                c(log(set.of.dil.values), rev(log(set.of.dil.values)))
              
              
              y.logis.poly <- c(logist.LOW.ci, rev(logist.HIGH.ci))
              
              
              if (logdil[length(logdil)] < logdil[1]) {
                x.logis.poly <-
                  c(rev(log(set.of.dil.values)), log(set.of.dil.values))
                
                y.logis.poly <-
                  c(rev(logist.LOW.ci), (logist.HIGH.ci))
              }
              
              
              
              
              
              # determine how intense confidence interval should be: darker=more confident
              howtransparent <-
                max(abs(
                  as.numeric(logist.HIGH.ci) - as.numeric(logist.LOW.ci)
                ), na.rm = T)
              if (is.nan(howtransparent))
                howtransparent <- 1
              howtransparent[howtransparent > 1] <- 1
              setalpha <- (255 * (1 - howtransparent))
              if (is.na(setalpha))
                setalpha <- 0
              if (!is.na(setalpha)) {
                if (setalpha < 20 | setalpha > 255)
                  setalpha <- 20
                curvecolor <-
                  makeTransparent(vircol, alpha = setalpha)
              }
              
              polygon(x.logis.poly,
                      y.logis.poly,
                      col = curvecolor,
                      border = NA)
              
              lines(
                log(set.of.dil.values),
                predicted.titercut[, "Prediction"],
                col = vircoldark,
                lwd = 3
              )
              
            }
            
            title(
              main = paste("Titer=", unlist(titer), sep = ""),
              line = -6,
              cex.main = 2
            )
            
            
          }
        }
        
        
        if (input$NIH.titer.estimation == TRUE) {
          mediumcount <- M * dil.values
          
          # I think fixed?
          average.per.dil <-
            colSums(t(plaques.per.sample), na.rm = T) / dim(plaques.per.sample)[2]
          
          if (length(which(!is.na(plaques.per.sample))) == 0)
            next
          
          included.plaques <- c()
          if (average.per.dil[1] <= mediumcount)
            included.plaques <- average.per.dil[1]
          
          if (length(included.plaques) > 0) {
            for (dil in 2:length(dilutions)) {
              if (is.na(average.per.dil[dil]))
                next
              if (average.per.dil[dil] <= mediumcount)
                included.plaques <-
                  c(included.plaques, average.per.dil[dil])
              if (average.per.dil[dil] > mediumcount &
                  average.per.dil[dil - 1] <= mediumcount)  {
                included.plaques <- c(included.plaques, average.per.dil[dil])
                break
              }
            }
          }
          
          these.dilutions <-
            dilutions[which(included.plaques %in% average.per.dil)]
          log.these.dil <- (these.dilutions)
          
          NIH.titer <- NA
          NIH.titer.confint <- c(NA, NA)
          NIH.titer.slope <- NA
          
          
          if (length((included.plaques)) < 1) {
            NIH.titer <- "outside limit of detection"
            
          }
          
          
          if (length(included.plaques) > 0) {
            NIH.curve.test <-
              try(drm(
                c(included.plaques / M) ~ log.these.dil,
                fct = LL.4(fixed = c(NA, bottom.asympt, 1, NA)),
                na.action = na.omit
              ))
            
            
            
            if (class(NIH.curve.test) == "try-error") {
              NIH.curve <-
                mean(c(log.these.dil[length(included.plaques)], log.these.dil[length(included.plaques) -
                                                                                1]))
              NIH.titer <-
                round((NIH.curve), digits = input$sig.figs.dil)
              NIH.titer.confint <- c(NA, NA)
              NIH.titer.slope <- NA
              
            }
            
            
            if (class(NIH.curve.test) != "try-error") {
              NIH.curve <-
                drm(
                  c(included.plaques / M) ~ log.these.dil,
                  fct = LL.4(fixed = c(NA, bottom.asympt, 1, NA)),
                  na.action = na.omit
                )
              
              if ((NIH.curve$coefficients["e:(Intercept)"]) > log.these.dil[length(included.plaques)])
                NIH.curve$coefficients["e:(Intercept)"] <-
                  log.these.dil[length(included.plaques)]
              
              suppressWarnings(predicted.titercut.NIH <-
                                 (
                                   predict(
                                     NIH.curve,
                                     interval = "confidence",
                                     data.frame(x = set.of.dil.values)
                                   )
                                 ))
              
              reduction.at.value <-
                which(round(predicted.titercut.NIH[, "Prediction"], digits = 1) == dil.values)
              
              if (length(reduction.at.value) < 1 |
                  sign(logdil[1] - logdil[length(logdil)]) != unname(sign(NIH.curve$coefficients["b:(Intercept)"])))   {
                if (sign(logdil[1] - logdil[length(logdil)]) == -1)
                  NIH.titer <- paste("<",
                                     min(
                                       c(low.titer.limit, upper.titer.limit),
                                       na.rm = T
                                     ),
                                     sep = "")
                
                if (sign(logdil[1] - logdil[length(logdil)]) == 1)
                  NIH.titer <- paste(">",
                                     max(
                                       c(low.titer.limit, upper.titer.limit),
                                       na.rm = T
                                     ), sep = "")
                
                next
              }
              
              
              NIH.titer <-
                round(
                  estimate.x.dose.value(
                    predicted.v = predicted.titercut.NIH[, "Prediction"],
                    dilvalues.v = dil.values,
                    set.of.dil.values.v = set.of.dil.values
                  ),
                  digits = input$sig.figs.dil
                )
              
              
              
              if (is.numeric(NIH.titer) &
                  NIH.titer < min(low.titer.limit, upper.titer.limit, na.rm =
                                  T))
                NIH.titer[NIH.titer < min(low.titer.limit, upper.titer.limit, na.rm =
                                            T)] <- paste("<",
                                                         min(low.titer.limit, upper.titer.limit, na.rm = T),
                                                         sep = "")
              
              if (is.numeric(NIH.titer) &
                  NIH.titer > max(low.titer.limit, upper.titer.limit, na.rm =
                                  T))
                NIH.titer[NIH.titer > max(low.titer.limit, upper.titer.limit, na.rm =
                                            T)] <- paste(">",
                                                         max(low.titer.limit, upper.titer.limit, na.rm = T),
                                                         sep = "")
              
              reduction.at.value.low <-
                which(round(predicted.titercut.NIH[, "Lower"], digits = 1) == dil.values)
              if (length(reduction.at.value.low) > 0)
                NIH.titer.confint[2] <-
                round(
                  estimate.x.dose.value(
                    predicted.v = predicted.titercut.NIH[, "Lower"],
                    dilvalues.v = dil.values,
                    set.of.dil.values.v = set.of.dil.values
                  ),
                  digits =
                    input$sig.figs.dil
                )
              
              if (length(reduction.at.value.low) == 0) {
                NIH.titer.confint[2] <-   "ci too large"
              }
              
              reduction.at.value.upper <-
                which(round(predicted.titercut.NIH[, "Upper"], digits = 1) == dil.values)
              if (length(reduction.at.value.upper) > 0)
                
                NIH.titer.confint[1] <-
                round(
                  estimate.x.dose.value(
                    predicted.v = predicted.titercut.NIH[, "Upper"],
                    dilvalues.v = dil.values,
                    set.of.dil.values.v = set.of.dil.values
                  ),
                  digits =
                    input$sig.figs.dil
                )
              
              if (length(reduction.at.value.upper) == 0) {
                NIH.titer.confint[1] <-   "ci too large"
              }
              
              NIH.titer.slope <-
                round(NIH.curve$coefficients["b:(Intercept)"],
                      digits = input$sig.figs.dil + 2)
              
              
              
              
              if (!is.na(NIH.titer)) {
                # for figure
                
                
                logist.LOW.ci <- predicted.titercut.NIH[, "Lower"]
                logist.HIGH.ci <-
                  predicted.titercut.NIH[, "Upper"]
                
                x.logis.poly <-
                  c(log(set.of.dil.values), rev(log(set.of.dil.values)))
                
                
                y.logis.poly <-
                  c(logist.LOW.ci, rev(logist.HIGH.ci))
                
                if (logdil[length(logdil)] < logdil[1]) {
                  x.logis.poly <-
                    c(rev(log(set.of.dil.values)), log(set.of.dil.values))
                  
                  y.logis.poly <-
                    c(rev(logist.LOW.ci), (logist.HIGH.ci))
                }
                
                # determine how intense confidence interval should be: darker=more confident
                howtransparent <-
                  max(abs(
                    as.numeric(logist.HIGH.ci) - as.numeric(logist.LOW.ci)
                  ), na.rm = T)
                if (is.nan(howtransparent))
                  howtransparent <- 1
                howtransparent[howtransparent > 1] <- 1
                setalpha <- (255 * (1 - howtransparent))
                if (is.na(setalpha))
                  setalpha <- 0
                if (!is.na(setalpha)) {
                  if (setalpha < 20 | setalpha > 255)
                    setalpha <- 20
                  curvecolor <-
                    makeTransparent(vircol, alpha = setalpha)
                }
                
                polygon(x.logis.poly,
                        y.logis.poly,
                        col = curvecolor,
                        border = NA)
                
                lines(
                  log(set.of.dil.values),
                  predicted.titercut.NIH[, "Prediction"],
                  col = vircoldark,
                  lwd = 3
                )
                title(
                  main = paste("Titer=", unlist(NIH.titer), sep = ""),
                  line = -6,
                  cex.main = 2
                )
                
                
              }
              
              
              
            }
            
            
          }
          
          
          table.of.titers[3, i] <- NIH.titer
          table.of.titers[4:5, i] <- NIH.titer.confint
          table.of.titers[6, i] <- NIH.titer.slope
          
          
        }
        
      }
      
      dev.off()
      
      
      if (input$save.neut.data == "Yes")   {
        if (is.null(input$set_neut_output_directory))  {
          output$print_warnings_neut <-
            renderText(
              "You need to select an output directory using the button 'Please select a directory for saving neutralization data output'."
            )
          next
        }
        
        req(input$set_neut_output_directory)
        directory.to.save.data <-
          parseDirPath(volumes, input$set_neut_output_directory)
        
        neut_image_name <-
          paste(
            directory.to.save.data,
            "/",
            plaquetables.names.no.ext[j],
            "-",
            experiment.name,
            ".png",
            sep = ""
          )
        
        
        neutim <- readImage(image.file.temp.name)
        writeImage(neutim, neut_image_name)
        
        write.table(
          table.of.titers,
          paste(
            directory.to.save.data,
            "/",
            plaquetables.names.no.ext[j],
            "-",
            experiment.name,
            "-data.xls",
            sep = ""
          ),
          sep = "\t",
          col.names = NA
        )
        
      }
      
    }
    
    
    
    
    output$neut_image <- renderImage({
      neutim <- readImage(image.file.temp.name)
      
      list(src = image.file.temp.name,
           contentType = "image/png",
           alt = "This text appears when something is wrong?")
      
    })
    
    
  })
  
  
  
  
  
  
  
  
  estimate.target.virus.titer.for.neut.assay <-
    function(titer.virus.stock = titer.virus.stock,
             target.plaque.count = target.plaque.count,
             final.volume.diluted.virus.required.ul = final.volume.diluted.virus.required.ul,
             volume.added.to.well = volume.added.to.well) {
      ul.for.first.dilution <- NA
      ul.for.final.dilution <- NA
      
      target.plaques.per.ml <-
        (target.plaque.count * 1000) / volume.added.to.well
      
      # required dilution to achieve target.plaques.per.ml
      required.dilution <-
        (10 ^ titer.virus.stock) / target.plaques.per.ml
      
      # estimate dilutions
      ul.for.final.dilution <-
        final.volume.diluted.virus.required.ul / required.dilution
      
      if (ul.for.final.dilution < 10 & ul.for.final.dilution >= 1) {
        ul.for.first.dilution <- 100
        ul.for.final.dilution <-
          final.volume.diluted.virus.required.ul / (required.dilution / 10)
        
      }
      
      if (ul.for.final.dilution < 1) {
        ul.for.first.dilution <- 10
        ul.for.final.dilution <-
          final.volume.diluted.virus.required.ul / (required.dilution / 100)
        
      }
      
      # Print out the virus titer for the virus stock
      paste(
        paste(
          "final target plaques per ml: ",
          round(target.plaques.per.ml, digits = 2),
          ", or 10^",
          round(log10(target.plaques.per.ml), digits = 2) ,
          ", or ",
          format(signif(target.plaques.per.ml, digit = 3), scientific = TRUE),
          sep = ""
        ),
        "\n",
        paste(
          "target dilution to achieve this number of plaques per ml: 1/",
          round(required.dilution, digits = 2),
          ", or: 10^-",
          round(log10(required.dilution), digits = 2) ,
          sep = ""
        ),
        "\n",
        paste("dilution step 1:", ul.for.first.dilution, "into 1000 ul"),
        "\n",
        paste(
          "dilution step 2:",
          round(ul.for.final.dilution, digits = 1),
          "into",
          final.volume.diluted.virus.required.ul,
          "ul"
        )
      )
      
      
    }
  
  
  observeEvent(input$estimate.virus.dilution, {
    estimated.targ.vir.neut  <-
      estimate.target.virus.titer.for.neut.assay(
        titer.virus.stock = input$titer.virus.stock,
        target.plaque.count = input$target.plaque.count,
        final.volume.diluted.virus.required.ul =
          input$final.volume.diluted.virus.required.ul,
        volume.added.to.well = input$volume.added.to.well
      )
    
    output$print_output_virus_dilution <-
      renderText(estimated.targ.vir.neut)
    
  })
  
  
  
  estimate.virus.titer <- function(log.10.dil = input$log.10.dil,
                                   plaque.counts.per.well = input$plaque.counts.per.well,
                                   starting.dil.on.plate = input$starting.dil.on.plate,
                                   startingvolume = input$startingvolume,
                                   dilution.series.virus.titration = input$dilution.series.virus.titration) {
    if (starting.dil.on.plate == 1)
      log.10.dil.full <-
        c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) - 1
    if (starting.dil.on.plate == 1 / 10)
      log.10.dil.full <- c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
    if (starting.dil.on.plate == 1 / 100)
      log.10.dil.full <-
        c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) + 1
    if (starting.dil.on.plate == 1 / 1000)
      log.10.dil.full <-
        c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) + 2
    
    log.10.dil <- log.10.dil.full[log.10.dil]
    
    starting.dil.on.plate
    
    plaques.per.ml.at.that.dilution <-
      (mean(plaque.counts.per.well) * 1000) / startingvolume
    
    #  multiply that value by the amount you diluted the initial virus stock
    total.infectious.virions.per.ml <-
      plaques.per.ml.at.that.dilution * (dilution.series.virus.titration ^ log.10.dil)
    
    # Print out the virus titer for the virus stock
    print(paste(
      "virus titer per ml: 10^",
      round(log10(total.infectious.virions.per.ml), digits = 2),
      " or ",
      format(
        signif(total.infectious.virions.per.ml, digit = 3),
        scientific = TRUE
      ),
      sep = ""
    ))
    
    
  }
  
  
  observeEvent(input$estimate.virus.titer.com, {
    numeric.plaque.count <-
      as.numeric(unlist(strsplit(input$plaque.counts.per.well, ",")))
    
    estimated.virus.titer.v <-
      estimate.virus.titer(
        log.10.dil = input$log.10.dil,
        plaque.counts.per.well =
          numeric.plaque.count,
        starting.dil.on.plate =
          input$starting.dil.on.plate,
        startingvolume = input$startingvolume
      )
    
    output$print_output_virus_titer <-
      renderText(estimated.virus.titer.v)
    
  })
  
  
  #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
  # Based on the directory selected for where the plate folders are, select the plates to trim
  #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
  
  
  shinyDirChoose(
    input,
    'select_plates_image_trimmer',
    roots = volumes,
    session = session,
    restrictions = system.file(package = 'base')
  )
  
  #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
  # Based on the plates folders, select the wells to trim
  #~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
  observeEvent(input$select_plates_image_trimmer, {
    updateSelectInput(session,
                      inputId = 'wells_to_trim',
                      choices = c("all", mixedsort(list.files(
                          parseDirPath(volumes, input$select_plates_image_trimmer)
                      ))))
    
  })
  
  
  shinyDirChoose(
    input,
    'set_trim_output_directory',
    roots = volumes,
    session = session,
    restrictions = system.file(package = 'base')
  )
  
  observeEvent(input$set_trim_output_directory, {
    output$print_set_trim_output_directory <-
      renderPrint({
        parseDirPath(volumes, input$set_trim_output_directory)
      })
  })
  
  
 
  observeEvent(input$submit.trim, {
    
  ret.dir.trim <-
      parseDirPath(volumes, input$select_plates_image_trimmer)
    
  
  ret.dir.trim.output <-
      parseDirPath(volumes, input$set_trim_output_directory)

  print(ret.dir.trim.output)
  
  wells.to.trim <- input$wells_to_trim
  
  directions.to.wells.to.trim <-
    paste(ret.dir.trim, "/", wells.to.trim, sep = "")
  
  for (i in 1:length(directions.to.wells.to.trim)) {
    
    im.to.trim <- readImage(directions.to.wells.to.trim[i])
    
    if (input$option_well_trim=="Trim individual well images") {
      
    image.dimensions <- dim(im.to.trim)[1:2]
    trimmed.image <- im.to.trim[1:min(image.dimensions),1:min(image.dimensions),]
    
    image.dimensions.text <- paste(c("Before trimming:",image.dimensions,"\nAfter trimming:",min(image.dimensions),min(image.dimensions)),collapse=" ")

        output$print_output_image_dimensions  <-
      renderText(image.dimensions.text)
    
      image.file.name.trim <-paste(
      ret.dir.trim.output,"/",
      sub("^([^.]*).*", "\\1", wells.to.trim[i]),
             ".jpg",
              sep = ""
            )
      
      writeImage(trimmed.image, image.file.name.trim,type="jpeg")
      summary.image.trim <- trimmed.image
    }
    
    if (input$option_well_trim=="Cut single large image into individual square wells") {
      
      image.dimensions <- dim(im.to.trim)
      
      rowsplits <- seq(input$grid.x.start,image.dimensions[1],input$dim.well)
      colsplits <- seq(input$grid.y.start,image.dimensions[2],input$dim.well)
      
      if (length(which(rowsplits>image.dimensions[1]))>0) rowsplits <- rowsplits[-which(rowsplits>image.dimensions[1])]
      if (length(which(colsplits>image.dimensions[2]))>0) colsplits <- colsplits[-which(colsplits>image.dimensions[2])]
      
 
      grid.im <- matrix(0,image.dimensions[1],image.dimensions[2])
      grid.im[,colsplits] <- 1
      grid.im[rowsplits,] <- 1
      
      
      if (input$option_print_cut_wells=="Yes") {
      for (x in 1:(length(rowsplits)-1)) {
        for (y in 1:(length(colsplits)-1)) {

          if (length(dim(im.to.trim))<3)  trimmed.image <- im.to.trim[rowsplits[x]:rowsplits[x+1],colsplits[y]:colsplits[y+1]]
          if (length(dim(im.to.trim))>=3)  trimmed.image <- im.to.trim[rowsplits[x]:rowsplits[x+1],colsplits[y]:colsplits[y+1],]

            image.file.name.trim <-paste(
            ret.dir.trim.output,"/",
            sub("^([^.]*).*", "\\1", wells.to.trim[i]),"-",x,"-",y,
            ".jpg",
            sep = ""
          )
            
          
  writeImage(trimmed.image, image.file.name.trim,type="jpeg")
        }
      }
      }
      
      summary.image.trim <-
        paintObjects(
          grid.im,
          im.to.trim,
          col = "red",
          thick = T
        )
      
      image.dimensions.text <- "grid image"
      
    }
    
    
  }

  
  
  output$im.to.trim <- renderImage({
    image.file.name.trim <- tempfile(fileext = ".png")

      writeImage(summary.image.trim,  image.file.name.trim)
    
    list(src = image.file.name.trim,
         contentType = "image/png",
         alt = "This text appears when something is wrong?")
    
  }, deleteFile = TRUE)
  
  
  })
  
  
  
  
  
})
leahkatzelnick/Viridot documentation built on Jan. 26, 2021, 4:48 a.m.