R/tileLevelClassifier.R

Defines functions tileLevelClassifier

Documented in tileLevelClassifier

#' Tile Level Classifier.
#'
#' The Images should be in this folder: file.path(homeDir,chipsName,flightName,tileName,Unclassified)
#'
#' @param homeDir Base Directory.  Needs "Chips" folder.
#' @param flightName Name of flight to classify
#' @param tileName Name of tile to classify
#' @param exportResults Logical.  Option to export results for later analysis
#' @param chipsName Folder name of the chips
#' @param modelObj A DIMEC model object.
#' @param filterThreshold
#' @param returnPlotData Logical. Option to return plotData from function.
#'
#' @return Returns either a data.frame of classifications, or outputs it to a
#'  plotData.csv, and two kml files, or both. Outputs to "getwd()/Model Output".
#'   Optionally exports the results as a set of files in directories, including
#'   positive images, kml's of positives, and the full model results
#'
#' @examples
#' \dontrun{
#'
#' modelLabel <- "M17"
#' modelDir <- file.path("Model Files",
#'                        modelLabel,
#'                        paste0("ModelFile-",modelLabel,".h5"))
#' model <- keras::load_model_hdf5(file.path(modelDir,
#'                                      paste0("ModelFile-",modelLabel,".h5")))
#' classNames <- read.table(file.path(modelDir, "classes.txt"))[,1]
#'
#'
#' ## Dos Rios Flight
#' setwd("F:/Adam Cummings/GoogleImagery/FirstPurchase/")
#' dirList <- list.dirs("Chips/ca_dosrios_20170813",recursive = FALSE,
#' full.names=FALSE) ##160 directories
#' tileLevelClassifier(flightName = "ca_dosrios_20170813",
#'                      tileName = "C15_R01-DosRios2016",
#'                       modelName=modelLabel,
#'                       exportResults = TRUE,
#'                       returnPlotData = FALSE,
#'                       classes=classNames)
#'
#' ##Use flightLevelClassifier to classify many tiles at once:
#'
#' flightLevelClassifier(dirList,
#'        flightName = "ca_dosrios_20170813",
#'        modelName=modelLabel,
#'        exportResults = TRUE,
#'        returnPlotData = FALSE,
#'        classes=classNames)
#'
#' ## This also works:
#' sapply(dirList,tileLevelClassifier,
#'        flightName = "ca_dosrios_20170813",
#'        modelName=modelLabel,
#'        exportResults = TRUE,
#'        returnPlotData = FALSE,
#'        classes=classNames)
#'}
#'
#' @export
tileLevelClassifier <- function(homeDir = getwd(),
                                chipsName = "Chips",
                                flightName,
                                tileName,
                                modelObj,
                                exportResults=TRUE,
                                returnPlotData=FALSE,
                                filterThreshold = 0.2){
  if(reticulate::py_is_null_xptr(modelObj$modelPtr)){
    message("Reloading Model")
    modelObj <- reloadDIMECModel(modelObj)
  }

  modelName <- modelObj$modelLabel
  classes <-  modelObj$classNames
  positiveClasses <- as.character(modelObj$positiveClasses)

  outputDir <- file.path(homeDir,"Model Output",flightName,tileName,modelName)
  flightDir <- file.path(homeDir,chipsName,flightName,tileName)
  ##Problem Check:
  if(dir.exists(outputDir) & exportResults)
    return(print(paste0(file.path(flightName,tileName),
                        " is already analyzed with ", modelName, ".")))
  if(length(list.dirs(flightDir))==1)
    stop(paste0("Make sure there is an Unclassified Dir at ", flightDir, "."))



  print(file.path(flightName,tileName))

  filesToClassifyLoc <- fs::dir_ls(flightDir,
                                   recurse = TRUE,
                                   type = "file",all=FALSE)
  #Remove Thumbs.db database hidden files:
  filesToClassifyLoc <- filesToClassifyLoc[!grepl(pattern = ".db",x = filesToClassifyLoc)]

  filesToClassify <- basename(filesToClassifyLoc)
  testSamples <- length(filesToClassify)

  test_generator <- keras::flow_images_from_directory(
    flightDir, #Test folder
    target_size = c(299, 299),
    color_mode = "rgb",
    #class_mode = "categorical",
    batch_size = 1,
    shuffle = FALSE,
    seed = 123)

  preds <- keras::predict_generator(modelObj$modelPtr,
                                    test_generator,
                                    verbose = 1,
                                    steps = testSamples,
                                    max_queue_size = 1,
                                    workers=4)

  plotData <- stats::setNames(data.frame(preds),classes)

  plotData$Image <- filesToClassify
  plotData$Model_Prediction <- classes[apply(plotData[,seq_along(classes)],
                                            1,which.max)]
  if(all(positiveClasses %in% classes)){
    if(length(positiveClasses) > 1)
      plotData$PositiveTotal <- apply(plotData[,positiveClasses],1,sum)
    if(length(positiveClasses) == 1)
      plotData$PositiveTotal <- plotData[,positiveClasses]
  } else{
    plotData$PositiveTotal <- NA
  }


  ##OUTPUT
  if(exportResults){

    latLong <- filenameExtractor(as.character(plotData$Image))

    plotData <- cbind(plotData,latLong[,c(3,2)])

    dir.create(outputDir, recursive=TRUE)
    write.csv(plotData,file = file.path(outputDir,"plotData.csv"))

    if(all(positiveClasses %in% classes)){

    }
    sapply(positiveClasses,function(x) {
      topImageExporter(plotData,
                       homeDir = homeDir,
                       class = x,
                       flightName=flightName,
                       tileName = tileName,
                       modelName = modelName,
                       threshold = filterThreshold)
    })

    ## KML:
    if(length(positiveClasses)>1){
      kmlSubset <-  plotData[apply(plotData[,positiveClasses] > filterThreshold,
                                   1,any),]
    } else{
      kmlSubset <-  plotData[plotData[,positiveClasses] > filterThreshold,]
    }

    kmlSubset2 <- subset(plotData, Model_Prediction %in% positiveClasses)

    kmlMaker(plotData,
             fileName = paste0(flightName,"-FULL-",modelName),
             exportDir = outputDir,
             layerName = paste0("TotalExport--",modelName))

    if(nrow(kmlSubset) > 0){
      kmlMaker(kmlSubset,
               fileName = paste0(flightName,"-",modelName),
               exportDir = outputDir,
               layerName = paste0("HighProbPoints--",modelName))
    }

    if(nrow(kmlSubset2) > 0){
      kmlMaker(kmlSubset2,
               fileName = paste0(flightName,"-Predicted-",modelName),
               exportDir = outputDir,
               layerName = paste0("Predicted--",modelName))
    }


  }

  if(returnPlotData) return(plotData)
}
adamkc/DIMEChelper documentation built on Feb. 1, 2020, 12:24 p.m.