ropensci / magick

Magic, madness, heaven, sin
https://docs.ropensci.org/magick
Other
462 stars 65 forks source link

image_read() dropping frames #373

Closed 1010shane closed 1 year ago

1010shane commented 1 year ago

Hello, trying to run this simple tutorial on the gganimate page for combining multiple gifs. The repex they provided (shown below) fails with an Error: subscript out of bounds error in the final for loop which is sequentially combining individual frames to create the final gif.

library(dplyr)
library(ggplot2)
library(magick)
library(gganimate)

A<-rnorm(100,50,10)
B<-rnorm(100,50,10)
DV <- c(A,B)
IV <- rep(c("A","B"),each=100)
sims <- rep(rep(1:10,each=10),2)
df<-data.frame(sims,IV,DV)

means_df <- df %>%
               group_by(sims,IV) %>%
               summarize(means=mean(DV),
                         sem = sd(DV)/sqrt(length(DV)))

stats_df <- df %>%
              group_by(sims) %>%
              summarize(ts = t.test(DV~IV,var.equal=TRUE)$statistic)

a <- ggplot(means_df, aes(x = IV,y = means, fill = IV)) +
  geom_bar(stat = "identity") +
  geom_point(aes(x = IV, y = DV), data = df, alpha = .25) +
  geom_errorbar(aes(ymin = means - sem, ymax = means + sem), width = .2) +
  theme_classic() +
  transition_states(
    states = sims,
    transition_length = 2,
    state_length = 1
  ) + 
  enter_fade() + 
  exit_shrink() +
  ease_aes('sine-in-out')

a_gif <- animate(a, width = 240, height = 240)

b <- ggplot(stats_df, aes(x = ts))+
  geom_vline(aes(xintercept = ts, frame = sims))+
  geom_line(aes(x=x,y=y),
            data = data.frame(x = seq(-5,5, .1),
                              y = dt(seq(-5,5, .1), df = 18))) +
  theme_classic() +
  ylab("density") +
  xlab("t value") +
  transition_states(
    states = sims,
    transition_length = 2,
    state_length = 1
  ) +
  enter_fade() + 
  exit_shrink() +
  ease_aes('sine-in-out')

b_gif <- animate(b, width = 240, height = 240)

a_mgif <- image_read(a_gif)
b_mgif <- image_read(b_gif)

new_gif <- image_append(c(a_mgif[1], b_mgif[1]))
for(i in 2:100){
  combined <- image_append(c(a_mgif[i], b_mgif[i]))
  new_gif <- c(new_gif, combined)
}

new_gif

When I check the length of a_mgif and b_mgif, I get the following:

> a_mgif %>% length()
[1] 71
> b_mgif %>% length()
[1] 62

It seems that image_read() is truncating the number of frames in the gifs (which should be 100). I have tried fixing this by manually setting the number of frames of the gif to 100 in the animate() function, and have tried the coalesce = FALSE argument within image_read() to no avail. Any advice?

> sessionInfo()
R version 4.1.2 (2021-11-01)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 22.04.2 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.10.0
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.10.0

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8     LC_MONETARY=en_US.UTF-8   
 [6] LC_MESSAGES=en_US.UTF-8    LC_PAPER=en_US.UTF-8       LC_NAME=C                  LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] gganimate_1.0.8 magick_2.7.4    ggplot2_3.4.2   dplyr_1.1.2    

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.10       rstudioapi_0.14   magrittr_2.0.3    hms_1.1.3         progress_1.2.2    tidyselect_1.2.0  munsell_0.5.0     colorspace_2.1-0 
 [9] R6_2.5.1          rlang_1.1.1       fansi_1.0.4       tools_4.1.2       grid_4.1.2        gtable_0.3.3      utf8_1.2.3        gifski_1.12.0-1  
[17] cli_3.6.1         withr_2.5.0       tibble_3.2.1      lifecycle_1.0.3   crayon_1.5.2      farver_2.1.1      tweenr_2.0.2      vctrs_0.6.3      
[25] glue_1.6.2        labeling_0.4.2    stringi_1.7.12    compiler_4.1.2    pillar_1.9.0      prettyunits_1.1.1 generics_0.1.3    scales_1.2.1     
[33] pkgconfig_2.0.3  
jeroen commented 1 year ago

Thanks. It looks like there has been a change of behavior in the gifski renderer that creates the gifs. It is probably trying to be smart and skipping duplicate frames and instead increasing the playing time for those frames.

A workaround is to adapt the animate() calls to use the magick renderer which creates a_mgif and b_mgif directly without the need to use image_read:

a_mgif <- animate(a, width = 240, height = 240, renderer = magick_renderer())
b_mgif <- animate(b, width = 240, height = 240,  renderer = magick_renderer())
# a_mgif <- image_read(a_gif)
# b_mgif <- image_read(b_gif)