R/imagehistory.R

Defines functions shinyload

Documented in shinyload

library(EBImage)                             #Include EBImage Lib
library(R6)

#used to keep track of rotate, crop, or any other function that requires order
Queue <- setRefClass(Class = "Queue",
                     fields = list(
                       name = "character",
                       data = "list"
                     ),
                     methods = list(
                       size = function() {
                         'Returns the number of items in the queue.'
                         return(length(data))
                       },
                       #
                       push = function(item) {
                         'Inserts element at back of the queue.'
                         data[[size()+1]] <<- item
                       },
                       #
                       pop = function() {
                         'Removes and returns head of queue (or raises error if queue is empty).'
                         if (size() == 0) stop("queue is empty!")
                         value <- data[[1]]
                         data[[1]] <<- NULL
                         value
                       },
                       reverse_pop = function() {
                        'Removes tail of queue'
                        if (size() == 0) stop("queue is empty!")
                        value <- data[[size()]]
                        data[[size()]] <<- NULL
                        value
                       },
                       #
                       poll = function() {
                         'Removes and returns head of queue (or NULL if queue is empty).'
                         if (size() == 0) return(NULL)
                         else pop()
                       },
                       #
                       peek = function(pos = c(1)) {
                         'Returns (but does not remove) specified positions in queue (or NULL if any one of them is not available).'
                         if (size() < max(pos)) return(NULL)
                         #
                         if (length(pos) == 1) return(data[[pos]])
                         else return(data[pos])
                       },
                       initialize=function(...) {
                         callSuper(...)
                         #
                         # Initialise fields here (place holder)...
                         #
                         .self
                       }
                     )
)

#' Class providing object describing one action.
#'
#' @docType class
#' @importFrom R6 R6Class
#' @export
#' @keywords data
#' @return Object of \code{\link{R6Class}} representing a single ShinyImage action.
#' @format \code{\link{R6Class}} object.
#' @examples
#' crop = c(c(0, 0), c(1200, 1400))
#' siaction$new(0.1, 1, 0, crop, 1, 0, 0)
#' @field brightness Stores address of your lightning server.
#' @field contrast Stores id of your current session on the server.
#' @field gamma Stores url of the last visualization created by this object.
#' @field crop A double nested sequence of crops c\(c\(x1, y1\), c\(x2, y2\)\).
#' @field blur stores value of blur
#' @field rotate stores value of rotate 
#' @field grayscale stores value of colormode (1 if grayscale, 0 if color)
#' #' @section Methods:
#' \describe{
#'   \item{Documentation}{The user should not need to create an action object. This is a class used exclusively by a shinyimg to keep track of a set of changes.}
#'   \item{\code{new(brightness, contrast, gamma, crop)}}{This method is used to create object of this class with the appropriate parameters.}
#'
#'   \item{\code{get_action()}}{This method returns a c() list of the input parameters.}
#' }  
#'
siaction <- R6Class("siaction",
                    # Make this action mutable. TODO: Make it so that
                    # it doesn't need to be
                    lock_objects = FALSE,
                    public = list(
                      # Initialize all the values of this action
                      initialize = function(brightness, contrast, gamma, crop, blur, rotate, grayscale, flip, flop) {
                        private$brightness <- brightness
                        private$contrast <- contrast
                        private$gamma <- gamma
                        private$crop <- crop
                        private$blur <- blur
                        private$rotate <- rotate
                        private$grayscale <- grayscale
                        private$flip <- flip
                        private$flop <- flop
                      },
                      # Get the c()'d properties of this particular action
                      get_action = function() {
                        # Note that because private$crop consists of 4 values,
                        # it contributes 4 slots to this vector.
                        return (c(private$brightness, 
                                  private$contrast, 
                                  private$gamma, 
                                  private$crop, 
                                  private$blur, 
                                  private$rotate, 
                                  private$grayscale,
                                  private$flip, 
                                  private$flop))
                      }
                    ),
                    private = list(
                      # Properties of this action
                      brightness = 0,
                      contrast = 0,
                      gamma = 0,
                      crop = NULL,  # vector of length 4
                      blur = 0, 
                      rotate = 0, 
                      grayscale = 0, 
                      flip = 0, 
                      flop = 0
                    )
)

#' An EBImage wrapper with integrated history tracking.
#'
#' @docType class
#' @importFrom EBImage display imageData Image colorMode readImage 
#' writeImage gblur combine channel rotate
#' @importFrom R6 R6Class
#' @export
#' @keywords data
#' @return Object of \code{\link{R6Class}} with manipulation functions.
#' @format \code{\link{R6Class}} object.
#' @examples
#' 
#' small_tiger = shinyimg$new(system.file("images","tiger_small.jpg",package="ShinyImage"))
#'
#' small_tiger$add_brightness() # Adds brightness to image
#' 
#' small_tiger$undo() # Undoes the brightness addition
#'
#' small_tiger$redo() # Redoes the brightness addition
#' 
#' small_tiger$add_contrast() #Adds contrast to image 
#' 
#' small_tiger$remove_contrast() #Removes the contrast from the image
#'  
#' small_tiger$add_gamma() #Adds Gamma correction
#'
#' small_tiger$remove_gamma() #Removes the Gamma Correction
#' 
#' small_tiger$add_blur() #Adds blur to image 
#' 
#' small_tiger$remove_blur() #removes the blur from the image
#' 
#' small_tiger$add_rotate() #Adds rotation by 1 degree
#' 
#' small_tiger$remove_rotate() #Remoes the rotation by 1 degree
#' 
#' small_tiger$set_brightness(.5) #Sets brightness by number inputted
#' 
#' small_tiger$set_contrast(2) #Sets contrast by number inputted 
#' 
#' small_tiger$set_gamma(2) #Sets gamma by number inputted
#' 
#' small_tiger$set_blur(6) #sets blurring to number inputted
#' 
#' small_tiger$set_rotate(180) #sets rotationt to degrees inputted
#' 
#' small_tiger$set_grayscale(1) #changes image to colormode
#' 
#' small_tiger$set_grayscale(0) #reverts back to colormode
#' 
#' small_tiger$crop(0,10,0,10) #crops by the coordinates inputted
#' 
#' small_tiger$undo #undoes the crop
#' 
#' small_tiger$gethistory() #returns recorded changes to image
#' 
#' small_tiger$save('save.si') # Saves the current state. The filename is optional.
#' 
#' small_tiger$saveImage('temp.jpg') #Saves current image to jpg file
#' 
#' small_tiger$load('save.si') # Loads from a previously saved state. The filename is optional. 
#' #Requires a previously instantiated shinyimg instance (argument provided to new can be null).
#'
#' @section Methods:
#' \describe{
#'   \item{Documentation}{The user should not need to create an action object. This is a class used exclusively by a shinyimg to keep track of a set of changes.}
#'   \item{\code{new(img)}}{Default constructor. \code{img} can be either a URL or a location of a local image.}
#'   \item{\code{undo()}}{Undoes the last change done to this image. When the original image state is reached, no more undos are possible.}
#'   \item{\code{redo()}}{Redos the next action after an undo has been performed. Will no longer redo if there are no more undos to redo.}
#'   \item{\code{canUndo()}}{Checks if can undo; used by Shiny.}
#'   \item{\code{redo()}}{Redos the next action after an undo has been performed without autorendering; used by Shiny. 
#'      Will no longer redo if there are no more undos to redo.}
#'   \item{\code{canRedo()}}{Checks if can redo; used by Shiny.}
#'   \item{\code{copy()}}{Returns a copy of the image.}
#'   \item{\code{add_brightness()}}{Adds brightness to the image.}
#'   \item{\code{remove_brightness()}}{Removes brightness (darkens) to the image.}
#'   \item{\code{add_contrast()}}{Adds contrast to the image.}
#'   \item{\code{remove_contrast()}}{Removes contrast from the image.}
#'   \item{\code{add_gamma()}}{Adds gamma correction to the image.}
#'   \item{\code{remove_gamma()}}{Remoevs gamma correction from the image.}
#'   \item{\code{add_blur()}}{Adds blur to the entire photo.}
#'   \item{\code{remove_blur()}}{Removes blur from the entire photo.}
#'   \item{\code{add_rotate()}}{Rotates image to the right.}
#'   \item{\code{remove_rotate()}}{Rotates image to the left.}
#'   \item{\code{set_brightness()}}{Sets the brightness of the image by number inputted.}
#'   \item{\code{set_contrast()}}{Sets the contrast of the image by number inputted.}
#'   \item{\code{set_gamma()}}{Sets the gamma correction of the image by number inputted.}
#'   \item{\code{set_blur()}}{Sets the blur of the image by number inputted.}
#'   \item{\code{set_rotate()}}{Sets the degree of rotation of the image by number inputted.}
#'   \item{\code{set_grayscale((num))}}{Sets the image to grayscale if 1 is inputted; Reverts the image back to colormode if 0 is inputted}
#'   \item{\code{change_color_mode()}}{Toggles between grayscale and colormode.}
#'   \item{\code{crop()}}{Uses locator to get corners of an image. Automatically finds min and max coordinates. 
#'     After two points are selected, a cropping selection can be create in order to crop the image to the desired size.
#'     If crop coordinates are passed, uses those instead of asking user.}
#'   \item{\code{flip_horizontally()}}{Flips image around horizontal axis.}
#'   \item{\code{flop_vertically()}}{Flops image around vertical axis.}
#'   \item{\code{get_raw()}}{Gets the raw matrix slices of the current image.}
#'   \item{\code{gethistory()}}{Returns a copy of the members of the shinyimg object stored in myhistory.}
#'   \item{\code{get_brightness()}}{Returns a copy of the value stored for brightness.}
#'   \item{\code{get_contrast()}}{Returns a copy of the value stored for contrast.}
#'   \item{\code{get_gamma()}}{Returns a copy of the value stored for gamma correction.}
#'   \item{\code{get_blur()}}{Returns a copy of the value stored for blur.}
#'   \item{\code{get_rotate()}}{Returns a copy of the value stored for rotation.}
#'   \item{\code{get_color()}}{Returns a copy of the value stored for grayscale/colormode.}
#'   \item{\code{get_flip()}}{Returns copy of the value stored for flip (1 means flipped).}
#'   \item{\code{get_flop()}}{Returns copy of the value stored for flop (1 means flopped).}
#'   \item{\code{get_imghistory()}}{Returns a copy of the list of image histories.}
#'   \item{\code{get_actions()}}{Returns a copy of the list of the input parameters.}
#'   \item{\code{get_history_directory()}}{Gets directory of the history.R file.}
#'   \item{\code{save(filepath)}}{Saves the current state to be resumed later. \code{filepath} has a default value of 'workspace.si'}
#'   \item{\code{saveImage(filepath)}}{Saves a jpg of the image.}
#'   \item{\code{load(filepath)}}{Loads a previously saved state. \code{filepath} has a default value of 'workspace.si'}
#'   \item{\code{size()}}{Returns the current image dimentions.}
#'   \item{\code{render()}}{Renders the current image.}
#'   }
#'   
shinyimg <- R6Class("shinyimg",
                    lock_objects = FALSE,
                    public = list(
                      logged_image = format(Sys.time(), "image_%b%y_%X"),
                      # Constructor of the shinyimg class
                      initialize = function(inputImage = NULL, 
                                            autosave_filename = NULL) {

                        self$logged_image <<- format(Sys.time(), "image_%b%y_%X")
                        #update name when initializing image
                        cat(self$logged_image,' <- shinyimg$new(\'',inputImage,'\')\n',sep='',file='~/history.R',append=TRUE)
                        self$set_default()
                        private$startup(inputImage, autosave_filename)
                      },
                      # Resets this object's values to the default ones.
                      set_default = function() {
                        cat(self$logged_image,'$set_default()\n',sep='',file='~/history.R',append=TRUE)

                        # Default brightness
                        private$myhistory = c(0,0,0,0,0,0,0,0,0,0,0,0,0)
                        private$brightness = 0
                        # Default Contrast
                        private$contrast = 1
                        # Default Gamma 
                        private$gamma = 1
                        # Default Blur
                        private$blur = 0
                        # Default Rotate
                        private$rotate = 0
                        # Default grayscale 
                        private$grayscale = 0 
                        # Default flip (flips an image horizontally) 
                        private$flip = 0
                        # Default flop (flips an image vertically)
                        private$flop = 0
                        # CURRENT Number of actions. Can be less than the
                        # Actual number of actions due to undos.
                        private$actions = 0
                        # Crop coordinates
                        private$xy1 = c(0, 0)
                        private$xy2 = NULL
                        # Crop offsets 
                        # (relative to top left, which is 0, 0)
                        private$xoffset = 0
                        private$yoffset = 0
                        # List of image histories (instances of "siaction")
                        private$img_history = c()
                        # Variable to store the source image
                        private$local_img = NULL
                        # Variable to store the current image to display
                        private$current_image = NULL
                        # The filename used for the autosave in 
                        # case of crashes
                        #private$autosave_filename = "workspace.si"
                        #heere
                        private$autosave_filename = 
                          paste(format(Sys.time(), "%b%d%H%M%S"), "-", "workspace.si",sep="")
                        # bool value to determine if user can undo 
                        private$order_list = Queue$new()
                        # Queue that stores non-commutative actions
                        # currently only supports rotate and crop
                        private$indexed_images <- vector("list")
                        # list that stores copies of all versions 
                        # of si objects created
                      },
                      # Function to write the current state of the program to 
                      # file.
                      save = function(file = private$autosave_filename) {
                        cat(self$logged_image,'$save(',file,')\n',sep="",file='~/history.R',append=TRUE)

                        # Generated action matrix done in O(1) time.
                        action_matrix <- matrix(NA, 
                                                nrow=length(private$img_history), 
                                                ncol=12)
                        # Fill in the history data
                        i = 1
                        for (item in private$img_history) {
                          history <- item$get_action()
                          # TODO: Map function perhaps?
                          action_matrix[i, ] <- c(history[1], history[2], 
                                                  history[3], history[4], 
                                                  history[5], history[6], 
                                                  history[7], history[8],
                                                  history[9], history[10],
                                                  history[11], history[12])
                          i = i + 1
                        }
                        # Save the current action number
                        actions <- private$actions
                        # Save the current image as well
                        img <- imageData(private$local_img)

                        order_list = private$order_list
                        indexed_images = private$indexed_images

                        # Save everything to file.
                        base::save(action_matrix, actions, img,
                          order_list, indexed_images, file=file)
                      },
                      # Counterpart to the save function, will load from
                      # previous save file.
                      load = function(file = private$autosave_filename) {
                        cat(self$logged_image,'$load(',file,')\n',sep="",file='~/history.R',append=TRUE)
                        base::load(file)
                        # Generate the image history.
                        private$img_history = c()
                        
                        private$local_img <- Image(img)

                        private$order_list = order_list
                        private$indexed_images = indexed_images
                        
                        # Not sure if this fixes the issue -- Had
                        # some weird color issues like loading in
                        # black and white. This seems to have fixed the
                        # issue.
                        colorMode(private$local_img) = Color
                        
                        # FIll in the action matrix
                        for (i in 1:dim(action_matrix)[1]) {
                          # private$add_action(action_matrix[i, 1],
                          #                    action_matrix[i, 2],
                          #                    action_matrix[i, 3],
                          #                    action_matrix[i, 4],
                          #                    action_matrix[i, 5],
                          #                    action_matrix[i, 6],
                          #                    action_matrix[i, 7],
                          #                    action_matrix[i, 8],
                          #                    action_matrix[i, 9],
                          #                    action_matrix[i, 10]
                          # )
                          private$update_img_history(
                                             action_matrix[i, 1],
                                             action_matrix[i, 2],
                                             action_matrix[i, 3],
                                             action_matrix[i, 4],
                                             action_matrix[i, 5],
                                             action_matrix[i, 6],
                                             action_matrix[i, 7],
                                             action_matrix[i, 8],
                                             action_matrix[i, 9],
                                             action_matrix[i, 10],
                                             action_matrix[i, 11],
                                             action_matrix[i, 12]
                          )
                        }
                        private$actions <- actions
                        
                        # Apply the latest action
                        private$update_all_img_values()

                        private$current_image <<- private$indexed_images[[private$actions]]

                        private$render()
                      },
                      # Uses the actions list (img_history) to undo the last
                      # done action. DOES NOT PRUNE THE LIST AT THIS POINT. 
                      undo = function() {
                        cat(self$logged_image,'$undo()\n',sep='',file='~/history.R',append=TRUE)

                        # If there are more actions to undo besides the 
                        # original
                        # image (aka action #1)
                        if (private$actions != 1) {
                          private$doUndo()
                          
                          display(private$current_image, method = "raster")                          
                        } else {
                          # There are no actions to undo.
                          print("No action to undo")
                        }
                      },
                      # undo function for Shiny app
                      canUndo = function() {
                        # If there are more actions to undo besides the 
                        # original
                        # image (aka action #1)
                        return (private$actions > 1)
                      },
                      # Uses the actions list (img_history) to redo the last
                      # undone action.
                      redo = function() {
                        cat(self$logged_image,'$redo()\n',sep='',file='~/history.R',append=TRUE)
                        # If there are actions to redo
                        if (private$actions < length(private$img_history)) {
                          private$doRedo()
                          display(private$current_image, method = "raster")
                        } else {
                          # No actions to redo.
                          print("No action to redo")
                        }
                      },
                      # Redo action for Shiny app
                      canRedo = function() {
                        cat(self$logged_image,'$redo()\n',sep='',file='~/history.R',append=TRUE)
                        # If there are actions to redo
                        return (private$actions < length(private$img_history))
                      },
                      # Returns a copy of this image. 
                      # One copy's changes will not affect the other.
                      copy = function() {
                        cat(self$logged_image,'$copy()\n',sep='',file='~/history.R',append=TRUE)
                        #TODO: Different options for cloning, 
                        # like collapsing history
                        return (self$clone())
                      },
                      # Adjusts brightness by 0.1. This is a good increment
                      # but a variable brightness function should be added.
                      add_brightness = function() {
                        #adds function to the history log
                        cat(self$logged_image,'$add_brightness()\n',sep='',file='~/history.R',append=TRUE)
                        # Adds 0.1 brightness.
                        private$do_action(1, 0.1)
                      },
                      
                      # Adjusts brightness by -0.1. This is a good decrement
                      # but a variable brightness function should be added.
                      remove_brightness = function() {
                        #adds function to the history log
                        cat(self$logged_image,'$remove_brightness()\n',sep='',file='~/history.R',append=TRUE)
                        # removes 0.1 brightness.
                        private$do_action(1, -0.1)
                      },
                      
                      # Adjusts contrast by 0.1. This is a good increment
                      # but a variable contrast function should be added.
                      add_contrast = function() {
                        #adds function to the history log
                        cat(self$logged_image,'$add_contrast()\n',sep='',file='~/history.R',append=TRUE)
                        # Adds 0.1 contrast.
                        private$do_action(3, 0.1)
                      },
                      
                      # Adjusts contrast by -0.1. This is a good increment
                      # but a variable contrast function should be added.
                      remove_contrast = function() {
                        #adds function to the history log
                        cat(self$logged_image,'$remove_contrast()\n',sep='',file='~/history.R',append=TRUE)
                        # removes 0.1 contrast.
                        private$do_action(3, -0.1)
                      },
                      
                      # Adjusts gamma by 0.5
                      add_gamma = function() {
                        #adds function to the history log
                        cat(self$logged_image,'$add_gamma()\n',sep='',file='~/history.R',append=TRUE)
                        private$do_action(5, 0.5)
                      },
                      
                      # Adjusts gamma by -0.5
                      remove_gamma = function() {
                        #adds function to the history log
                        cat(self$logged_image,'$remove_gamma()\n',sep='',file='~/history.R',append=TRUE)
                        private$do_action(5, -0.5)
                      },

                      # Adjusts blur by 1
                      add_blur = function() {
                        #adds function to the history log
                        cat(self$logged_image,'$add_blur()\n',sep='',file='~/history.R',append=TRUE)
                        private$do_action(7, 1)
                      }, 

                      # Adjusts blur by -1
                      remove_blur = function() {
                        #adds function to the history log
                        cat(self$logged_image,'$remove_blur()\n',sep='',file='~/history.R',append=TRUE)
                        private$do_action(7, -1)
                      }, 

                      # Adjusts rotate by 1 degree
                      add_rotate = function() {
                        #adds function to the history log
                        cat(self$logged_image,'$add_rotate()\n',sep='',file='~/history.R',append=TRUE)
                        # need to add 1 to private$actions because it will only be updated 
                        # after it calls do_action
                        # private$add_order(2, private$actions + 1, 10, NULL, NULL, NULL)
                        # actionID = 9
                        # mutatorAmount = 10; how much rotate is changing
                        # isNonCommutative = TRUE
                        # whichNonCommAction = 2 (bc its rotate, not crop)
                        private$do_action(9, 10, TRUE, 2)
                      }, 

                      # Adjusts rotate by -1 degree
                      remove_rotate = function() {
                        #adds function to the history log
                        cat(self$logged_image,'$remove_rotate()\n',sep='',file='~/history.R',append=TRUE)
                        # need to add 1 to private$actions because it will only be updated 
                        # after it calls mutator
                        # private$add_order(2, private$actions + 1, -10, NULL, NULL, NULL)
                        # private$mutator(9, -10)

                        # actionID = 9
                        # mutatorAmount = -10; how much rotate is changing
                        # isNonCommutative = TRUE
                        # whichNonCommAction = 2 (bc its rotate, not crop)
                        private$do_action(9, -10, TRUE, 2)
                      },

                      set_brightness = function(brightness) {
                        #adds function to the history log
                        cat(self$logged_image,'$set_brightness(',brightness,')\n',sep="",file='~/history.R',append=TRUE)
                        # Sets brightness.
                        private$do_action(2, brightness)
                      },
                      
                      set_contrast = function(contrast) {
                        #adds function to the history log
                        cat(self$logged_image,'$set_contrast(',contrast,')\n',sep="",file='~/history.R',append=TRUE)
                        # Sets brightness.
                        private$do_action(4, contrast)
                      },

                      set_gamma = function(gamma) {
                        #adds function to the history log
                        cat(self$logged_image,'$set_gamma(',gamma,')\n',sep="",file='~/history.R',append=TRUE)
                        private$do_action(6, gamma)
                        # Sets gamma correction
                      },

                      set_blur = function(blur) {
                        #adds function to the history log
                        cat(self$logged_image,'$set_blur(',blur,')\n',sep="",file='~/history.R',append=TRUE)
                        # Sets blur
                        private$do_action(8, blur)
                      }, 

                      set_rotate = function(rotate) {
                        #adds function to the history log
                        cat(self$logged_image,'$set_rotate(',rotate,')\n',sep="",file='~/history.R',append=TRUE)
                        # need to add 1 to private$actions because it will only be updated 
                        # after it calls mutator
                        # private$add_order(2, private$actions + 1, rotate - private$rotate, NULL, NULL, NULL)
                        # # Sets rotation of image
                        # private$mutator(10, rotate)

                        # private$add_order(2, private$actions + 1, 10, NULL, NULL, NULL)
                        # actionID = 9 (9 was for add/remove rotate but we need to keep it
                        # for set_rotate so we can pass in the relative value for updating queue)
                        # mutatorAmount = 10; how much rotate is changing
                        # isNonCommutative = TRUE
                        # whichNonCommAction = 2 (bc its rotate, not crop)
                        private$do_action(9, rotate - private$rotate, TRUE, 2)
                      },

                      set_grayscale = function(grayscale) {
                        #adds function to the history log
                        cat(self$logged_image,'$set_grayscale(',grayscale,')\n',sep="",file='~/history.R',append=TRUE)
                        # Sets image to grayscale if argument is 1
                        # Else image is colormode
                        # Can revert image to colormode if argument is 0
                        private$do_action(11, grayscale)
                      },

                      change_color_mode = function() {
                        cat(self$logged_image,'$change_color_mode()\n',sep="",file='~/history.R',append=TRUE)
                        if (private$grayscale == 1)
                          private$do_action(11, 0)
                        else
                          private$do_action(11, 1)
                      },

                      # The command line cropper uses locator to have the
                      # user locate the two corners of the subimage. 
                      crop = function(x1, x2, y1, y2) {
                        # If crop coordinates not given, get them from the user.
                        if (missing(x1) || missing(x2) || missing(y1) || missing(y2))
                        {
                          print("Select the two opposite corners 
                                of a rectangle on the plot.")
                          location = locator(2)
                          x1 = min(location$x[1], location$x[2])
                          y1 = min(location$y[1], location$y[2])
                          x2 = max(location$x[1], location$x[2])
                          y2 = max(location$y[1], location$y[2])
                        }

                        # NM:  for history file
                        cmd <- paste('crop(',
                           x1, ',', x2, ',', y1, ',', y2, ')', sep='')
                        cat(self$logged_image,'$',cmd,'\n',sep='',file='~/history.R',append=TRUE)

                        private$do_action(12, c(x1=x1,x2=x2,y1=y1,y2=y2), TRUE, 1)
                      },

                      # flips an image around the horizontal axis
                      flip_horizontally = function() {
                        cat(self$logged_image,'$flip_horizontally()\n',sep="",file='~/history.R',append=TRUE)
                        if (private$flip == 0)
                        {
                        	flip <- 1
                        }
                        if (private$flip == 1)
                        {
                        	flip <- 0
                        }

                        # actionID = 13 for flip
                        # mutatorAmount = flip (how much flip is changing -- either 1 or 0)
                        # isNonCommutative = TRUE
                        # whichNonCommAction = 3 (bc its flip, not crop or rotate or flop)
                        private$do_action(13, flip, TRUE, 3)

                      }, 

                      # flops an image around the vertical axis
                      flop_vertically = function() {
                        cat(self$logged_image,'$flop_horizontally()\n',sep="",file='~/history.R',append=TRUE)
                        # flips image
                        if (private$flop == 0)
                        {
                        	flop <- 1
                        }
                        if (private$flop == 1)
                        {
                        	flop <- 0
                        }

                        # actionID = 14 for flop
                        # mutatorAmount = flop (how much flip is changing -- either 1 or 0)
                        # isNonCommutative = TRUE
                        # whichNonCommAction = 4 (bc its flop, not crop or rotate or flip)
                        private$do_action(14, flop, TRUE, 4)
                      },
   
                      # Returns the size of the current image.
                      # Needed for Shiny to determine the max values of
                      # the sliders. 
                      size = function() {
                        dim(private$current_image)
                      },
                      # Gets the raw matrix slices of the current image
                      get_raw = function() {
                        return (imageData(private$current_img))
                      }, 
                      # saves image as a jpeg 
                      saveImage = function(file) {
                        cat(self$logged_image,'$','saveImage(',file,')\n',sep="",file='~/history.R',append=TRUE)
                        if(missing(file))
                        {
                          writeImage(private$current_image, files = "temp.jpg")
                        }
                        else
                          writeImage(private$current_image, file)
                      }, 
                      # returns a copy of the members of the shinyimg object
                      gethistory = function() {
                      	return(private$myhistory)
                      }, 
                      # returns a copy of the brightness value
                      get_brightness = function() {
                        return (private$brightness)
                      },
                      # returns a copy of the contrast value
                      get_contrast = function() {
                        return (private$contrast)
                      },
                      # returns a copy of the gamma value
                      get_gamma = function() {
                        return (private$gamma)
                      },
                      # returns a copy of the blur value
                      get_blur = function() {
                        return (private$blur)
                      }, 
                      # returns a copy of the rotate value
                      get_rotate = function() {
                        return(private$rotate)
                      },
                      # returns a copy of the grayscale value
                      # indicates colormode
                      get_color = function() {
                      	return(private$grayscale)
                  	  },
                      # returns a copy of the flip value
                      get_flip = function() {
                        return(private$flip)
                      }, 
                      # returns a copy of the flop value
                      get_flop = function() {
                        return(private$flop)
                      },
                      # returns a copy of the list of image histories
                      get_imghistory = function() { 
                        return(private$img_history)
                      }, 
                      # returns a copy of the list of input parameters
                      get_actions = function() {
                        return(private$actions)
                      }, 
                      #gets directory of history.R file 
                      get_history_directory = function() {
                      	setwd('~')
                      	return(cat('history.R is located at ', getwd(), '/history.R\n', sep = ""))
                      }
                      #Uses a matrix as the image. Can be used to reintegrate
                      # a get_raw generated matrix.
                      # Disabled as this feature could be abused. 
                      #import_matrix = function(m) {
                      #	private$set_default()
                      #	private$startup(NULL, NULL, m)
                      #}
                    ),
                    private = list(
                      # The following are the members of the shinyimg obj.
                      # Default brightness
                      myhistory = c(0,0,0,0,0,0,0,0,0,0,0,0,0),
                      #names(myhistory) <- c(1,2,3,4,5,6,7,8,9,10),
                      brightness = 0,
                      # Default Contrast
                      contrast = 1,
                      gamma = 1, 
                      blur = 0, 
                      rotate = 0, 
                      grayscale = 0,
                      flip = 0, 
                      flop = 0, 
                      # CURRENT Number of actions. Can be less than the
                      # Actual number of actions due to undos.
                      actions = 0,
                      # Crop coordinates
                      xy1 = c(0, 0),
                      xy2 = NULL,
                      # Crop offsets (relative to top left, which is 0, 0)
                      xoffset = 0,
                      yoffset = 0,
                      # List of image histories
                      img_history = c(),
                      # Variable to store the source image
                      local_img = NULL,
                      # Variable to store the current image to display
                      current_image = NULL,
                      # The filename used for the autosave in case of crashes
                      #autosave_filename = "workspace.si",
                      autosave_filename = 
                        paste(format(Sys.time(), "%b%d%H%M%S"), "-", "workspace.si",sep=""),

                      # refactoring all of the actions from the previous functions
                      # add_action, mtuator, and add_order
                      # calls a bunch of helpers
                      # isNonCommutative: bool value indicates if Non-Commutative (i.e. crop or rotate)
                      # whichNonCommAction: int to indicate if crop or rotate
                      # actionIndex: action ID #
                      # nonCommValues: values of crop or rotate
                      do_action = function(actionID, mutatorAmount, isNonCommutative = FALSE, whichNonCommAction = -1) {
   		               	  # default values for functions that modify nonComm

                        private$remove_potential_redos()
                      	
                        private$actions <- private$actions + 1

                        if (isNonCommutative)
                      	# do_action parameter that tells if non-commutative 
                      	# if non-commuatate
                        {
                      		private$update_queue(private$actions, whichNonCommAction, mutatorAmount)
                        }

                        private$update_img_amount(actionID, mutatorAmount)

                      	private$update_img_history()
                        private$generate_current_image()
                        private$generate_queued_image()
                        private$update_colormode()
                        # must have a generted image 
                      	private$update_saved_images()
                      	private$render()

                      },
                      doUndo = function() {
                        # Step back by one action
                          private$actions <- private$actions - 1
                          
                          # we go to our list that contains all the versions 
                          # of our si object 
                          # we go back to the action number 

                          private$update_all_img_values()

                          private$current_image <<- private$indexed_images[[private$actions]]
                      },
                      doRedo = function() {
                        # Increment by one action, then apply it
                          private$actions <- private$actions + 1
                          
                          # we go to our list that contains all the versions 
                          # of our si object 
                          # we go back to the action number 

                          private$update_all_img_values()

                          private$current_image <<- private$indexed_images[[private$actions]]
                      },
                      # prunes img_history and indexed_images
                      remove_potential_redos = function() {
                      	# private$actions is not updated for this function
                      	# only function to have an un-updated private$actions 
                      	# in do_action

                      	# If we are not at the most recent image, we need 
                        # to prune the extra actions for img_history
                        if (private$actions < 
                            length(private$img_history)) {
                          private$img_history <- 
                            private$img_history[1:private$actions]
                        }

						            # If we are not at the most recent image, we need 
                        # to prune the extra actions for indexed_images
                        if (private$actions < 
                            length(private$indexed_images)) {
                          private$indexed_images <- 
                            private$indexed_images[1:private$actions]
                        }

                        while (private$order_list$size() > 0 &&
                          private$actions
                          < private$order_list$peek(private$order_list$size())[["actionID"]])
                        {
                          private$order_list$reverse_pop()
                        }
                      },
                      update_queue = function(actionID, whichNonCommAction, nonCommValue) {
                        private$order_list$push(c(actionID=actionID,
                          whichNonCommAction=whichNonCommAction, nonCommValue=nonCommValue))
                      },

                      update_crop_values = function(cropValues) {
                        if (length(cropValues) != 4)
                          stop("Bad cropValues vector")

                        cropValues = as.list(cropValues)
                        x1 = cropValues$x1
                        x2 = cropValues$x2
                        y1 = cropValues$y1
                        y2 = cropValues$y2

                        # In order to maintain a correct cropping, 
                        # we need to know how much of
                        # the original image has already been cropped.
                        xdiff = x2 - x1
                        ydiff = y2 - y1
                        
                        # The offset is needed to maintain the ABSOLUTE 
                        # crop data.
                        private$xoffset = private$xoffset + x1
                        private$yoffset = private$yoffset + y1
                        
                        # Create the absolute crop data using the offsets
                        # and new area.
                        private$xy1 = c(private$xoffset, private$yoffset)
                        private$xy2 = c(private$xoffset + xdiff, 
                                        private$yoffset + ydiff)
                      },

                      # amount is a length 4 vector if the action is a crop.
                      update_img_amount = function(actionID, amount) {
                        switch(actionID,
                               # ActionID 1, brightness adjustment
                               private$brightness <- 
                                 private$brightness + amount,
                               
                               # ActionID 2, brightness setting
                               private$brightness <- amount,
                               
                               #  ActionID 3, contrast adjustment
                               private$contrast <- 
                                 private$contrast + amount,
                               
                               #  ActionID 4, contrast setting
                               private$contrast <- amount,

                               # ActionID 5, gamm adjustment
                               private$gamma <- 
                                 private$gamma + amount, 

                               # ActionID 6, gamma setting
                               private$gamma <- amount,

                               # Action ID 7, blur adjustment
                               private$blur <- 
                                 private$blur + amount, 

                               # Action ID 8, blur setting
                               private$blur <- amount,

                               # Action ID 9, rotate adjustment
                               private$rotate <- 
                                 private$rotate + amount, 

                               # Action ID 10, rotate setting
                               private$rotate <- amount,

                               # Action ID 11, grayscale setting
                               private$grayscale <- amount,

                               # Action ID 12, crop
                               private$update_crop_values(amount),

                               # Action ID 13, flip
                               private$flip <- amount,

                               # Action ID 14, flop
                               private$flop <- amount
                        )
                        # private$add_action()
                      },

                      update_img_history = function(bright = private$brightness, 
                                            cont = private$contrast, 
                                            gam = private$gamma, 
                                            crop1x = private$xy1[1],
                                            crop1y = private$xy1[2], 
                                            crop2x = private$xy2[1], 
                                            crop2y = private$xy2[2],
                                            blurring = private$blur,
                                            rotation = private$rotate, 
                                            colorMode = private$grayscale,
                                            flip_value = private$flip, 
                                            flop_value = private$flop) {                              
                        private$img_history <-
                          c(private$img_history, siaction$new(bright, 
                                                              cont, 
                                                              gam, 
                                                              c(
                                                                c(crop1x,crop1y), 
                                                                c(crop2x, crop2y)
                                                              ),
                                                              blurring, 
                                                              rotation, 
                                                              colorMode, 
                                                              flip_value, 
                                                              flop_value
                                                              ))
                      },

                      # Sets all image values to what they are/were for the
                      # current action.
                      update_all_img_values = function() {
                        # Get the vector of image values of the current action.
                        args = private$img_history[private$actions]
                        args = args[[1]]
                        args = args$get_action()

                        private$myhistory <- args

                        private$brightness = args[1]
                        private$contrast = args[2]
                        private$gamma = args[3]
                        # private$xy1 = c(args[4], args[5])
                        # private$xy2 = c(args[6], args[7])   
                        private$blur = args[8]
                        private$rotate = args[9]
                        private$grayscale = args[10]
                        private$flip = args[11]
                        private$flop = args[12]
                      },

                      generate_current_image = function() {
                        private$update_all_img_values()

                        if (private$blur > 0)
                        {
                          private$current_image <- 
                            gblur(private$local_img, sigma = private$blur)
                        } 

                        #need to fix blur back to original image
                        if (private$blur <= 0)
                        {
                          private$current_image <- private$local_img
                        }      

                        private$current_image <- 
                          private$current_image * private$contrast

                        private$current_image <- 
                          private$current_image + private$brightness

                        private$current_image <- 
                          private$current_image ^ private$gamma
                      },

                      generate_queued_image = function() {
                        order_copy <- private$order_list$copy()
                        while (order_copy$size() != 0)
                        {
                          popped <- order_copy$pop()
                          if (popped[2] == 1)
                          {
                            cropValues = as.list(popped[3:6])
                            x1 = cropValues$nonCommValue.x1
                            x2 = cropValues$nonCommValue.x2
                            y1 = cropValues$nonCommValue.y1
                            y2 = cropValues$nonCommValue.y2

                            private$current_image <- private$current_image[x1:x2, y1:y2,]
                          } 
                          else if (popped[2] == 2)
                          {
                            private$current_image <- rotate(private$current_image, popped[3])
                          }
                          else if (popped[2] == 3)
                          {
                            private$current_image <- flip(private$current_image)
                          }
                          else if (popped[2] == 4)
                          {
                            private$current_image <- flop(private$current_image)
                          }
                        }
                      },

                      update_colormode = function() {
                        if (private$grayscale == 1)
                          private$current_image <- channel(private$current_image, "gray")
                      },

                      update_saved_images = function() {
                        length <- length(private$indexed_images)
                        private$indexed_images[[length + 1]] <- private$current_image
                      },

                      render = function() {
                        if (!is.null(private$current_image)) {
                          display(private$current_image, method = "raster")
                        }
                      },
                      
                      # The matr argument imports a matrix as the image.
                      # The remaining two arguments are supplied by the 
                      # constructors for shinyimg.
                      startup = function(inputImage, autosave_filename, 
                                         matr = NULL) {
                        # Set the autosave filename if it is not null
                        if (!is.null(autosave_filename))
                          private$autosave_filename <- autosave_filename
                        
                        if (!is.null(inputImage)) {
                          # Here the user passed in an argument for 
                          # inputImage We use the readImage functionality
                          # to read in the image to form an EBImage. This
                          # may be changed at a later time. 
                          private$local_img <- readImage(inputImage)
                          # Here we set the current image to the original 
                          # image. Multiplying by one essentially copies 
                          # the image. The reason this works is that the
                          # multiplication function when applied to an 
                          # image changes contrast. In this case, 1 is 
                          # the default contrast, and thus we are 
                          # essentially making a copy by not changing 
                          # the contrast but telling it to make another
                          # image. 
                          private$current_image <- private$local_img * 1
                          private$update_saved_images()

                          # Here we set the xy2 coordinate, which is the
                          # lower right coordinate of the image. 
                          private$xy2 <- c(dim(private$local_img)[1], 
                                           dim(private$local_img)[2])
                          
                          # Add the "base" action, which is the original
                          # image. 
                          private$update_img_history()
                          private$actions <- private$actions + 1
                          private$render()
                          # 03/13 COMMENTED OUT 
                          # private$add_action()
                        } else if (!is.null(matr)) {
                          
                          # TODO: Possible that m is not actually a matrix.
                          # Could error. 
                          result = tryCatch({
                            private$local_img <- Image(matr)
                          }, warning = function(w) {
                            return
                          }, error = function(e) {
                            return
                          }, finally = {
                            
                          });
                          
                          # Here we set the current image to the original 
                          # image. Multiplying by one essentially copies 
                          # the image. The reason this works is that the
                          # multiplication function when applied to an 
                          # image changes contrast. In this case, 1 is 
                          # the default contrast, and thus we are 
                          # essentially making a copy by not changing 
                          # the contrast but telling it to make another 
                          # image. 
                          private$current_image <- private$local_img * 1
                          
                          # Here we set the xy2 coordinate, which is the 
                          # lower right coordinate of the image. 
                          private$xy2 <- c(dim(private$local_img)[1], 
                                           dim(private$local_img)[2])
                          
                          # Add the "base" action, which is the original 
                          # image. 
                          private$update_img_history()
                          private$actions <- private$actions + 1
                          private$render()
                          # 03/13 COMMENTED OUT 
                          # private$add_action()
                        } else {
                          # TODO: Maybe some sort of error message?
                          result = tryCatch({
                            private$load()
                          }, warning = function(w) {
                            
                          }, error = function(e) {
                            
                          }, finally = {
                            
                          });
                        }
                      }
                    )
)

#' Wrapper to load an image from a cold boot. 
#' 
#' @param filename The filename of a file previously generated by shinyimg's $save function. 
#' @examples
#' 
#' small_tiger = shinyimg$new(system.file("images","tiger_small.jpg",package="ShinyImage"))
#'
#' small_tiger$save("tiger.si")
#' # Restart R 
#' reloaded_tiger = shinyload("tiger.si")
#'
#' @export
shinyload = function(filename) {
  shinyimg$new(NULL, filename)
}
matloff/ShinyImage documentation built on May 21, 2019, 12:56 p.m.