将两个动画情节组合成一个 GIF/MP4

Posted

技术标签:

【中文标题】将两个动画情节组合成一个 GIF/MP4【英文标题】:Combining two animated plot into one GIF/MP4 【发布时间】:2019-09-21 21:17:47 【问题描述】:

我创建了两个基于同一数据集的动画时间序列图表。一个是显示总体趋势的折线图,一个是显示前 5 个子组的条形图。我想看看我是否可以将它们组合成一个时间序列动画,可以并排或与折线图一起作为小插图。

我已经构建了单独的动画,但我不清楚是否可以将它们组合起来,或者我是否需要创建一个新图像,每个帧都有插图,然后对其进行动画处理。

这是一个最小的示例数据集:

data <- data.frame(
  Year = c(rep(1988, 5), rep(1989, 5), rep(1990, 5)),
  Total = c(rep(1000, 5), rep(1100, 5), rep(1200, 5),
  SubGroup = c("A", "B", "C", "D", "E", "B", "A", "C", "D", E", "B", "C", "A", "E", "D"),
  Amount = c(200, 180, 100, 80, 60, 210, 200, 150, 100, 80, 250, 240, 200, 150, 110)
)

这是我的前 5 个条形图动画的代码:

# generate top 5 ranking by year

anim_table <- data %>%
  dplyr::group_by(Year) %>%
  dplyr::mutate(
    rank = min_rank(-Amount) * 1,
    Value_rel = Amount / Amount[rank == 1],
    Value_lbl = paste0(" ", Amount)
  ) %>%
  dplyr::filter(rank <= 5) %>%
  dplyr::ungroup() %>% 
  dplyr::arrange(Year, rank)

# create static barchart

p1 <- ggplot2::ggplot(anim_table, aes(rank)) +
  ggplot2::geom_tile(aes(
    y = Amount / 2,
    height = Amount,
    width = 0.9,
    fill = "blue"
  ), alpha = 0.8, color = NA) +
  ggplot2::geom_text(aes(y = 0, label = paste(SubGroup, " ")), size = 12, vjust = 0.2, hjust = 1) +
  ggplot2::geom_text(aes(y = Amount, label = Value_lbl, hjust = 0)) +
  ggplot2::coord_flip(clip = "off", expand = FALSE) +
  ggplot2::scale_y_continuous(labels = scales::comma) +
  ggplot2::scale_x_reverse() +
  ggplot2::guides(color = FALSE, fill = FALSE) +
  ggplot2::labs(
    title = "closest_state", x = "", y = "Amount",
    caption = "Source:  whatever"
  ) +
  ggplot2::theme(
    plot.title = element_text(color = "darkblue", face = "bold", hjust = 0, size = 30),
    axis.ticks.y = element_blank(),
    axis.text.y = element_blank(),
    plot.margin = margin(2, 2, 1, 16, "cm")
  ) +
# animate over Years
  gganimate::transition_states(Year, transition_length = 4, state_length = 1) +
  gganimate::ease_aes("cubic-in") 

gganimate::animate(p1, 200, fps = 5, duration = 100, width = 2000, height = 1200, renderer = gifski_renderer("anim1.gif"))

这是我的简单动画折线图的代码:


p2 <- ggplot2::ggplot(
  hc_total,
  aes(Year, Total)
) +
  ggplot2::geom_line() +
  ggplot2::scale_color_viridis_d() +
  ggplot2::labs(x = "Year", y = "Total") +
  gganimate::transition_reveal(Year)

gganimate::animate(p2, 200, fps = 5, duration = 100, width = 2000, height = 1200, renderer = gifski_renderer("anim2.gif"))

正在研究如何将这两个动画并排或以折线图作为插图组合成一个动画。

【问题讨论】:

你可以用 ImageMagick 做到这一点。您使用的是 Windows 还是 Linux? 我在 Mac 上工作 【参考方案1】:

您可以使用 ImageMagick 做到这一点。两个 GIF 必须具有相同的帧数,最好是相同的大小。

对于 Windows,我编写了这个运行 ImageMagick 的 R 函数:

appendGIFs <- function(gif1, gif2, gifout, vertically=FALSE, 
                        delay=20, convert = "C:/PortableApps/ImageMagick/convert")
  command <- sprintf("%s ( %s -coalesce -set page %s+0+0 -coalesce ) null: ( %s -coalesce ) -gravity %s -layers composite -set delay %d -loop 0 %s", 
                     convert, gif1, ifelse(vertically, "%[w]x%[fx:h*2]", "%[fx:w*2]x%[h]"), 
                     gif2, ifelse(vertically, "south", "east"),  delay, gifout)
  system(command) 

这里,gif1 是第一个 GIF 的路径,gif2 是第二个 GIF 的路径,gifout 是输出 GIF 的名称,convertconvert 可执行文件的路径ImageMagick (如果 ImageMagick 在您的路径中,您可以设置 convert = "magick convert")。

对于 Linux,这里是运行以并排追加的命令:

convert file1.gif'[0]' -coalesce \\( file2.gif'[0]' -coalesce \\) \\
          +append -channel A -evaluate set 0 +channel \\
          file1.gif -coalesce -delete 0 \\
          null: \\( file2.gif -coalesce \\) \\
          -gravity East -layers Composite output.gif

垂直追加:

convert file1.gif'[0]' -coalesce \\( file2.gif'[0]' -coalesce \\) \\
          -append -channel A -evaluate set 0 +channel \\
          file1.gif -coalesce -delete 0 \\
          null: \\( file2.gif -coalesce \\) \\
          -gravity South -layers Composite output.gif

对于 Mac,我不知道(我会尝试 Linux 方式)。

编辑

以下内容应该适用于任何操作系统。

appendGIFs <- function(gif1, gif2, gifout="result.gif", vertically=FALSE, 
                       delay=20, convert = "C:/PortableApps/ImageMagick/convert")
  currentdir <- getwd()
  on.exit(setwd(currentdir))
  tmpdir <- tempdir()
  invisible(file.remove(list.files(tmpdir, full.names = TRUE, pattern = "*.gif$")))
  file.copy(gif1, to = file.path(tmpdir, "gif1.gif"))
  file.copy(gif2, to = file.path(tmpdir, "gif2.gif"))
  setwd(tmpdir)
  command <- sprintf("%s gif1.gif -coalesce gif1-%%05d.gif", convert)
  system(command)
  command <- sprintf("%s gif2.gif -coalesce gif2-%%05d.gif", convert)
  system(command)
  nframes <- length(list.files(tmpdir, pattern = "^gif1-.*gif$"))
  for(i in 1:nframes)
    command <- sprintf("%s gif1-%05d.gif gif2-%05d.gif %sappend gif-%05d.gif", 
                       convert, i-1, i-1, ifelse(vertically, "-", "+"), i)
    system(command)
  
  command <- sprintf("%s -loop 0 -delay %d gif-*.gif result.gif", convert, delay)
  system(command)
  file.copy("result.gif", file.path(currentdir, gifout), overwrite=TRUE)

【讨论】:

看来我需要稍微调整一下语法才能让它在 Mac 上运行 - 我会试一试并报告!谢谢! 这很好 - 很抱歉延迟响应。非常感谢!

以上是关于将两个动画情节组合成一个 GIF/MP4的主要内容,如果未能解决你的问题,请参考以下文章

将两个事件处理组合成一个事件处理[重复]

将两个数组 排列组合到一个数组集合 求java 代码

将两个 RGB 图像组合成一个 6 通道图像 - openCV

如何将两个32位整数组合成一个64位整数?

MySQL - 使用 LIMIT 有效地将两个 select 语句组合成一个结果

python 两个列表组合成一个新的列表?