File: im.convert.R

package info (click to toggle)
r-cran-animation 2.7%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 1,268 kB
  • sloc: javascript: 873; sh: 15; makefile: 2
file content (172 lines) | stat: -rw-r--r-- 8,322 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#' A wrapper for the `convert' utility of ImageMagick or GraphicsMagick
#'
#' The main purpose of these two functions is to create GIF animations.
#'
#' The function \code{im.convert} simply wraps the arguments of the
#' \command{convert} utility of ImageMagick to make it easier to call
#' ImageMagick in R;
#' @rdname convert
#' @param files either a character vector of file names, or a single string
#'   containing wildcards (e.g. \file{Rplot*.png})
#' @param output the file name of the output (with proper extensions, e.g.
#'   \code{gif})
#' @param convert the \command{convert} command; it must be \code{'magick'},
#'   \code{'convert'} or \code{'gm convert'}; and it can be pre-specified as an
#'   option in \code{\link{ani.options}('convert')}, e.g. (Windows users)
#'   \code{ani.options(convert = 'c:/program
#'   files/imagemagick/convert.exe')}, or (Mac users) \code{ani.options(convert
#'   = '/opt/local/bin/convert')}; see the Note section for more details
#' @param cmd.fun a function to invoke the OS command; by default
#'   \code{\link{system}}
#' @param extra.opts additional options to be passed to \command{convert} (or
#'   \command{gm convert})
#' @param clean logical: delete the input \code{files} or not
#' @return The command for the conversion.
#'
#'   If \code{ani.options('autobrowse') == TRUE}, this function will also try to
#'   open the output automatically.
#' @note If \code{files} is a character vector, please make sure the order of
#'   filenames is correct! The first animation frame will be \code{files[1]},
#'   the second frame will be \code{files[2]}, ...
#'
#'   Both ImageMagick and GraphicsMagick may have a limit on the number of
#'   images to be converted. It is a known issue that this function can fail
#'   with more than (approximately) 9000 images. The function
#'   \code{\link{saveVideo}} is a better alternative in such a case.
#'
#'   Most Windows users do not have read the boring notes below after they have
#'   installed ImageMagick or GraphicsMagick. For the rest of Windows users:
#'
#'   \describe{
#'
#'   \item{\strong{ImageMagick users}}{Please install ImageMagick from
#'   \url{http://www.imagemagick.org}, and make sure the the path to
#'   \command{convert.exe} is in your \code{'PATH'} variable, in which case the
#'   command \command{convert} can be called without the full path.  Windows
#'   users are often very confused about the ImageMagick and \code{'PATH'}
#'   setting, so I'll try to search for ImageMagick in the Registry Hive by
#'   \code{readRegistry('SOFTWARE\ImageMagick\Current')$BinPath}, thus you might
#'   not really need to modify your \code{'PATH'} variable.
#'
#'   For Windows users who have installed LyX, I will also try to find the
#'   \command{convert} utility in the LyX installation directory, so they do not
#'   really have to install ImageMagick if LyX exists in their system (of
#'   course, the LyX should be installed with ImageMagick).
#'
#'   Once the \command{convert} utility is found, the animation option
#'   \code{'convert'} will be set (\code{ani.options(convert =
#'   'path/to/convert.exe')}); this can save time for searching for
#'   \command{convert} in the operating system next time.  }
#'
#'   \item{\strong{GraphicsMagick users}}{During the installation of
#'   GraphicsMagick, you will be asked if you allow it to change the PATH
#'   variable; please do check the option.  }
#'
#'   }
#'
#'   A reported problem is \code{cmd.fun = shell} might not work under Windows
#'   but \code{cmd.fun = system} works fine. Try this option in case of
#'   failures.
#' @author Yihui Xie
#' @family utilities
#' @references Examples at \url{https://yihui.org/animation/example/im-convert/}
#'
#'   ImageMagick: \url{http://www.imagemagick.org/script/convert.php}
#'
#'   GraphicsMagick: \url{http://www.graphicsmagick.org}
#' @export
im.convert = function(
  files, output = 'animation.gif', convert = c('magick', 'convert', 'gm convert'),
  cmd.fun = if (.Platform$OS.type == 'windows') shell else system, extra.opts = '', clean = FALSE
) {
  movie.name = output
  interval = head(ani.options('interval'), length(files))
  loop = ifelse(isTRUE(ani.options('loop')), 0, ani.options('loop'))
  convert = match.arg(convert, c('magick','convert', 'gm convert'))
  if (convert == 'magick' && requireNamespace('magick', quietly = TRUE)) {
    dispose_opt = regmatches(regexpr(pattern = "-dispose\\s+\\S+", text = extra.opts), x = extra.opts)
    dispose = gsub("-dispose ", "", dispose_opt)
    magick.convert(files = files, output = output, loop = loop, interval = interval, dispose = dispose)
    cmd = 0
  } else {
    if (convert == 'convert' || convert == "magick") {
      version = ''
      if (!is.null(ani.options('convert'))) {
        try(version <- cmd.fun(sprintf('%s --version', shQuote(ani.options('convert'))), intern = TRUE))
      }
      if (!length(grep('ImageMagick', version))) {
        try(version <- cmd.fun(sprintf('%s --version', convert), intern = TRUE))
      } else convert = ani.options('convert')
      if (!length(grep('ImageMagick', version))) {
        message('I cannot find ImageMagick with convert = ', shQuote(convert))
        convert_switch = ifelse(convert == 'convert',"magick","convert")
        try(version <- cmd.fun(sprintf('%s --version', convert_switch), intern = TRUE))
        if (!length(grep('ImageMagick', version))) {
          message('I also cannot find ImageMagick with convert = ', shQuote(convert_switch))
          if (.Platform$OS.type != 'windows' || is.null(convert <- find_magic())) {
            warning('Please install ImageMagick first or put its bin path into the system PATH variable')
            return()
          }
        }else{
          message('I find ImageMagick with convert = ', shQuote(convert_switch),". I will use " ,
                  shQuote(convert_switch)," instead of ", shQuote(convert),"!")
          convert = convert_switch
        }
      }
    } else {
      ## GraphicsMagick
      version = ''
      if (!is.null(ani.options('convert')))
        try(version <- cmd.fun(sprintf('%s -version', shQuote(ani.options('convert'))), intern = TRUE))
      if (!length(grep('GraphicsMagick', version))) {
        try(version <- cmd.fun(sprintf('%s -version', convert), intern = TRUE))
        if (!length(grep('GraphicsMagick', version))) {
          warning('I cannot find GraphicsMagick with convert = ', shQuote(convert),
                  '; you may have to put the path of GraphicsMagick in the PATH variable.')
          return()
        }
      } else convert = ani.options('convert')
    }

    convert = sprintf(
      '%s -loop %s %s %s %s', convert, loop,
      extra.opts, paste(
        '-delay', interval * 100,
        if (length(interval) == 1) paste(files, collapse = ' ') else files,
        collapse = ' '),
      shQuote(movie.name)
    )
    # there might be an error "the input line is too long", and we need to quote
    # the command; see http://stackoverflow.com/q/682799/559676
    if (.Platform$OS.type == 'windows') convert = sprintf('"%s"', convert)
    message('Executing: ', strwrap(convert, exdent = 4, prefix = '\n'))
    if (interactive()) flush.console()
    cmd = cmd.fun(convert)
    ## if fails on Windows using shell(), try system() instead of shell()
    if (cmd != 0 && .Platform$OS.type == 'windows' && identical(cmd.fun, shell)) {
      cmd = system(convert)
    }
  }
  if (cmd == 0) {
    message('Output at: ', output)
    if (clean) unlink(files)
    if (file.exists(output)) auto_browse(output)
  } else message('an error occurred in the conversion... see Notes in ?im.convert')
  invisible(convert)
}
#' @details The function \code{gm.convert} is a wrapper for the command
#'   \command{gm convert} of GraphicsMagick.
#' @rdname convert
#' @param ... arguments to be passed to \code{\link{im.convert}}
#' @export
gm.convert = function(..., convert = 'gm convert') {
  im.convert(..., convert = convert)
}

magick.convert = function(files, output, interval = 1, loop = 0, dispose = NULL){
  if (!length(dispose)) dispose = "background"
  dispose = tolower(dispose)
  img = magick::image_read(files, strip = TRUE)
  anim = magick::image_animate(img, loop = loop, fps = 100 / as.integer(interval * 100), dispose = dispose)
  magick::image_write(anim, path = output)
}