はじめに
R言語で画像処理を行うmagick
パッケージを確認します。
この記事では、image_animate()
とimage_write_gif()
またはimage_write_video()
を使ってアニメーション(gifファイル・mp4ファイル)を作成します。
【目次】
magickパッケージによるアニメーションの作成
magick
パッケージを利用してアニメーション(gif画像・mp4動画)を作成します。アニメーションに利用する画像のサイズが同じ場合と異なる場合で必要な処理が変わります。それぞれ確認します。
この記事では、ggplot2
パッケージとpatchwork
パッケージを利用して2つのグラフ並べた図を作成し画像ファイルとして保存しておき、作成した画像をまとめてアニメーションを作成します。作図処理について不要であれば、「画像の作成」を読み飛ばしてください。また、この記事での作図コードやデータフレームは、あくまで参考図を作るためのものです。必要な部分を汲み取って利用してください。
利用するパッケージを読み込みます。
# 利用パッケージ library(tidyverse) library(patchwork) library(magick)
この記事では、基本的にパッケージ名::関数名()
の記法を使うので、パッケージを読み込む必要はありません。ただし、作図コードがごちゃごちゃしないようにパッケージ名を省略しているためggplot2
と、patchwork
パッケージの演算子を使うためpatchwork
を読み込む必要があります。
また、ネイティブパイプ演算子|>
を使っています。magrittr
パッケージのパイプ演算子%>%
に置き換えても処理できますが、その場合はmagrittr
も読み込む必要があります。
画像サイズが共通の場合
まずは、アニメーションに利用する画像ファイルが全て同じサイズの場合の処理を確認します。
画像の作成
アニメーションに利用する画像ファイルを作成します。ガウス-ガンマ分布の確率変数(サンプル)と、そのサンプルをパラメータとするガウス分布の関係を例とします。ガウス-ガンマ分布とガンマ分布の関係については「【R】ガウス-ガンマ分布から確率分布の生成 - からっぽのしょこ」を参照してください。
作図コード(クリックで展開)
ガウス-ガンマ分布のパラメータ(ハイパーパラメータ)と、ガウス分布のパラメータとして利用する値(ガウス-ガンマの確率変数の値)を指定します。
# ガウス-ガンマ分布のパラメータを指定 m <- 0 beta <- 2 a <- 5 b <- 6 # ガウス-ガンマ分布の確率変数として利用する値を指定 lambda_i <- seq(from = 0, to = 2.5, length.out = 101) mu_i <- seq(from = -1.5, to = 1.5, length.out = 101) # ガウス分布のパラメータを格納 param_df <- tibble::tibble( mu = mu_i, lambda = lambda_i ) # フレーム数を設定 frame_num <- nrow(param_df) param_df
## # A tibble: 101 × 2 ## mu lambda ## <dbl> <dbl> ## 1 -1.5 0 ## 2 -1.47 0.025 ## 3 -1.44 0.05 ## 4 -1.41 0.075 ## 5 -1.38 0.1 ## 6 -1.35 0.125 ## 7 -1.32 0.15 ## 8 -1.29 0.175 ## 9 -1.26 0.2 ## 10 -1.23 0.225 ## # … with 91 more rows
作図用に、ガウス分布のパラメータをデータフレームに格納しておきます。パラメータの数(データフレームの行数)をフレーム数とします。
ガウス-ガンマ分布の(作図用の)確率変数の値を作成して、点ごとに確率密度を計算します。
# ガウス-ガンマ分布の確率変数の期待値を計算 E_mu <- m E_lambda <- a / b # ガウス-ガンマ分布の確率変数の値を作成 mu_vals <- seq( from = E_mu - 1/sqrt(beta*E_lambda) * 4, to = E_mu + 1/sqrt(beta*E_lambda) * 4, length.out = 200 ) lambda_vals <- seq(from = 0, to = E_lambda * 3, length.out = 200) # ガウス-ガンマ分布を計算 gaussian_gamma_df <- tidyr::expand_grid( mu = mu_vals, # 確率変数μ lambda = lambda_vals # 確率変数λ ) |> # 確率変数(μとλの格子点)を作成 dplyr::mutate( N_dens = dnorm(x = mu, mean = m, sd = 1/sqrt(beta*lambda)), # μの確率密度 Gam_dens = dgamma(x = lambda, shape = a, rate = b), # λの確率密度 density = N_dens * Gam_dens # 確率密度 ) gaussian_gamma_df
## # A tibble: 40,000 × 5 ## mu lambda N_dens Gam_dens density ## <dbl> <dbl> <dbl> <dbl> <dbl> ## 1 -3.10 0 0 0 0 ## 2 -3.10 0.0126 0.0561 0.00000748 0.000000420 ## 3 -3.10 0.0251 0.0703 0.000111 0.00000780 ## 4 -3.10 0.0377 0.0763 0.000521 0.0000398 ## 5 -3.10 0.0503 0.0781 0.00153 0.000119 ## 6 -3.10 0.0628 0.0774 0.00346 0.000268 ## 7 -3.10 0.0754 0.0751 0.00665 0.000500 ## 8 -3.10 0.0879 0.0719 0.0114 0.000822 ## 9 -3.10 0.101 0.0682 0.0181 0.00123 ## 10 -3.10 0.113 0.0641 0.0269 0.00172 ## # … with 39,990 more rows
ガウス分布の確率密度はdnorm()
、ガンマ分布の確率密度はdgamma()
で計算できます。
目安として描画する用に、ガウス分布のパラメータの期待値(ガウス-ガンマ分布の確率変数の期待値)による確率密度を計算します。
# ガウス分布の確率変数の値を作成 x_vals <- mu_vals # パラメータの期待値によるガウス分布を計算 E_gaussian_df <- tidyr::tibble( x = x_vals, # 確率変数 density = dnorm(x = x_vals, mean = E_mu, sd = 1/sqrt(E_lambda)) # 確率密度 ) E_gaussian_df
## # A tibble: 200 × 2 ## x density ## <dbl> <dbl> ## 1 -3.10 0.00667 ## 2 -3.07 0.00723 ## 3 -3.04 0.00782 ## 4 -3.00 0.00846 ## 5 -2.97 0.00914 ## 6 -2.94 0.00987 ## 7 -2.91 0.0106 ## 8 -2.88 0.0115 ## 9 -2.85 0.0124 ## 10 -2.82 0.0133 ## # … with 190 more rows
ガウス-ガンマ分布とガウス分布のグラフを確認します。
作図コード(クリックで展開)
# ガウス-ガンマ分布を作図 gaussian_gamma_graph <- ggplot() + geom_contour_filled(data = gaussian_gamma_df, mapping = aes(x = mu, y = lambda, z = density, fill = ..level..), alpha = 0.8) + # パラメータの生成分布 geom_point(mapping = aes(x = E_mu, y = E_lambda), color = "red", size = 5, shape = 4) + # パラメータの期待値 labs(title = "Gaussian-Gamma Distribution", subtitle = parse(text = paste0("list(m==", m, ", beta==", beta, ", a==", a, ", b==", b, ")")), fill = "density", x = expression(mu), y = expression(lambda)) # ガウス分布を作図 gaussian_graph <- ggplot() + geom_line(data = E_gaussian_df, mapping = aes(x = x, y = density), color = "red", size = 1, linetype = "dashed") + # 期待値による分布 labs(title = "Gaussian Distribution", subtitle = parse(text = paste0("list(E(mu)==", E_mu, ", E(lambda)==", round(E_lambda, 3), ")")), x = expression(x), y = "density") # ラベル # グラフを並べて描画 graph <- gaussian_gamma_graph / gaussian_graph + patchwork::plot_layout(guides = "collect") graph
{patchwork}
の/
演算子を使うと上下にグラフを並べて描画できます。
上の図では、ガウス-ガンマ分布の等高線上にパラメータの期待値を赤色のバツ印で示しています。また下の図では、パラメータの期待値によるガウス分布を赤色の破線で示しています。
パラメータのサンプルごとに、サンプルの点とサンプルによる分布のグラフを重ねて描画し、画像ファイルとして保存します。
# 保存用のフォルダを作成 dir_path <- "tmp_folder" dir.create(path = dir_path) # パラメータのサンプルごとに作図 for(i in 1:frame_num) { # i番目のパラメータを取得 mu <- mu_i[i] lambda <- lambda_i[i] # パラメータラベルを作成 hyparam_text <- paste0( "list(", "m==", m, ", beta==", beta, ", a==", a, ", b==", b, ", E(mu)==", E_mu, ", E(lambda)==", round(E_lambda, 3), ")" ) param_text <- paste0( "list(mu==", round(mu, 2), ", lambda==", round(lambda, 2), ")" ) # ガウス-ガンマ分布を作図 gaussian_gamma_graph <- ggplot() + geom_contour_filled(data = gaussian_gamma_df, mapping = aes(x = mu, y = lambda, z = density, fill = ..level..), alpha = 0.8) + # パラメータの生成分布 geom_point(mapping = aes(x = E_mu, y = E_lambda), color = "red", size = 5, shape = 4) + # パラメータの期待値 geom_point(data = param_df[i, ], mapping = aes(x = mu, y = lambda), color = "gold", size = 5) + # パラメータのサンプル labs(title = "Gaussian-Gamma Distribution", subtitle = parse(text = hyparam_text), fill = "density", x = expression(mu), y = expression(lambda)) # ガウス分布を計算 gaussian_df <- tibble::tibble( x = x_vals, # 確率変数 density = dnorm(x = x_vals, mean = mu, sd = 1/sqrt(lambda)) # 確率密度 ) # ガウス分布を作図 gaussian_graph <- ggplot() + geom_line(data = E_gaussian_df, mapping = aes(x = x, y = density), color = "red", size = 1, linetype = "dashed") + # 期待値による分布 geom_line(data = gaussian_df, mapping = aes(x = x, y = density), color = "gold", size = 1) + # サンプルによる分布 coord_cartesian(ylim = c(0, 0.7)) + # 描画範囲 labs(title = "Gaussian Distribution", subtitle = parse(text = param_text), x = expression(x), y = "density") # ラベル # グラフを並べて描画 graph <- gaussian_gamma_graph / gaussian_graph + patchwork::plot_layout(guides = "collect") # ファイルを書き出し file_path <- paste0(dir_path, "/", stringr::str_pad(i, width = 3, pad = "0"), ".png") # widthはframe_numの桁数 ggplot2::ggsave(filename = file_path, plot = graph, width = 800, height = 800, units = "px", dpi = 100) # 途中経過を表示 print(paste0(i, " (", round(i/frame_num*100, 1), "%)")) }
保存用のフォルダは空である必要があります。frame_num
枚の画像(グラフ)が保存されます。
動画の作成
続いて、複数枚の画像ファイルを1つの動画ファイルにします。
ファイルパスを作成します。
# ファイル名を取得 file_name_vec <- list.files(dir_path) # ファイルパスを作成 file_path_vec <- paste0(dir_path, "/", file_name_vec) file_path_vec[1:5]
## [1] "tmp_folder/001.png" "tmp_folder/002.png" "tmp_folder/003.png" ## [4] "tmp_folder/004.png" "tmp_folder/005.png"
list.files()
でdir_path
フォルダ内のファイル名を取得して、ファイルパスを作成します。
画像ファイルを読み込んで、gifファイルに変換します。
# 画像ファイルを読み込み pic_data_vec <- magick::image_read(path = file_path_vec) # gif画像を作成 gif_data <- magick::image_animate(image = pic_data_vec, fps = 1, dispose = "previous") gif_data
image_read()
で画像を読み込んで、image_animate()
でgifファイルに変換します。fps
引数に1秒当たりのフレーム数を指定します。
gif画像またはmp4動画として書き出します。
# gifファイルを書き出し magick::image_write_gif(image = gif_data, path = "GaussianGamma.gif", delay = 0.1) # mp4ファイルを書き出し magick::image_write_video(image = gif_data, path = "GaussianGamma.mp4", framerate = 10)
image_write_gif()
でgifファイル、image_write_video()
でmp4ファイルとして書き出します。delay
引数は1フレーム当たりの表示秒数、framerate
引数は1秒当たりのフレーム数を指定できます。1秒当たり10フレームとするのであれば、delay
引数に1/10
、framerate
引数に10
を指定します。
ファイルの書き出し時に、保存先のファイルパスを出力します。(書き出しと言いつつ、レンダリングしてますよね?image_animate()
との役割の違いがよく分かりません。)
動画ファイルは貼れないので省略します。
全ての画像サイズが同じ場合は、image_animate()
を使わなくてもアニメーションを作成できます(質に違いがあるかも?)。
# gifファイルを書き出し magick::image_write_gif(image = pic_data_vec, path = "GaussianGamma.gif", delay = 0.1) # mp4ファイルを書き出し magick::image_write_video(image = pic_data_vec, path = "GaussianGamma.mp4", framerate = 10)
詳しくは、次で確認します。
画像サイズが異なる場合
次は、アニメーションに利用する画像ファイルが異なるサイズの場合の処理を確認します。gifファイルと同様なのでmp4ファイルについては省略します。
画像の作成
アニメーションに利用する画像ファイルを作成します。フレームごとにが1ずつ大きくまたは小さくなる座標上ののヒートマップを例とします。
作図コード(クリックで展開)
フレーム数を指定して、x軸とy軸の値を作成しz軸の値を計算します。
# フレーム数を指定 frame_num <- 10 # 座標の下限を指定 lower_x <- 4 lower_y <- 3 # 値を作成 x_vals <- seq(from = 1, to = lower_x+frame_num, by = 1) y_vals <- seq(from = 1, to = lower_y+frame_num, by = 1) # 作図用のデータフレームを作成 df <- tidyr::expand_grid(x = x_vals, y = y_vals) |> # 格子点を作成 dplyr::mutate(z = x + y) df
## # A tibble: 182 × 3 ## x y z ## <dbl> <dbl> <dbl> ## 1 1 1 2 ## 2 1 2 3 ## 3 1 3 4 ## 4 1 4 5 ## 5 1 5 6 ## 6 1 6 7 ## 7 1 7 8 ## 8 1 8 9 ## 9 1 9 10 ## 10 1 10 11 ## # … with 172 more rows
x軸の値をx_vals
、y軸の値をy_vals
を作成して、x_vals, y_vals
の格子状の点(全ての組み合わせ)をexpand_grid()
で作成しx軸とy軸の値を足します。
ヒートマップを確認します。
作図コード(クリックで展開)
# ヒートマップを確認 ggplot(data = df, mapping = aes(x = x, y = y, fill = z)) + # ヒートマップ geom_tile(show.legend = FALSE) + geom_text(mapping = aes(label = paste0("(", x, ", ", y, ")")), color = "white", size = 5) + # 座標ラベル scale_fill_gradient(low ="gold", high = "red") + # グラデーション scale_x_continuous(breaks = x_vals) + scale_y_reverse(breaks = y_vals) + # y軸を反転 coord_equal(clip = "off", expand = FALSE) + # 余白を非表示 labs(title = "Heatmap", subtitle = parse(text = paste0("list(x==list(1, cdots, ", lower_x+frame_num, ")", ", y==list(1, cdots, ", lower_y+frame_num, "))")))
geom_tile()
でヒートマップを描画します。格子点を渡す必要があります。
この図を最大の描画範囲として、座標(描画範囲)を変化させます。
lower_x+1, lower_y+1
のサイズから、lower_x+frame_num, lower_y+frame_num
のサイズまでを描画し、画像ファイルとして保存します。
# 保存用のフォルダを作成 dir_path <- "tmp_folder" dir.create(path = dir_path) # フレームごとに作図 for(i in 1:frame_num) { # i番目のデータを抽出 tmp_df <- df |> dplyr::filter(x <= lower_x+i, y <= lower_y+i) # ヒートマップを作成 g <- ggplot(data = tmp_df, mapping = aes(x = x, y = y, fill = z)) + geom_tile(show.legend = FALSE) + # ヒートマップ geom_text(mapping = aes(label = paste0("(", x, ", ", y, ")")), color = "white", size = 5) + # 座標ラベル scale_fill_gradient(low ="gold", high = "red") + # グラデーション scale_x_continuous(breaks = x_vals) + scale_y_reverse(breaks = y_vals) + # y軸を反転 coord_equal(clip = "off", expand = FALSE) + # 余白を非常時 labs(title = "Heatmap", subtitle = parse(text = paste0("list(x==list(1, cdots, ", lower_x+i, ")", ", y==list(1, cdots, ", lower_y+i, "))"))) # ファイルを書き出し file_path <- paste0(dir_path, "/", stringr::str_pad(i, width = 2, pad = "0"), ".png") # widthはframe_numの桁数 ggplot2::ggsave( filename = file_path, plot = g, width = (lower_x+i)*100, height = (lower_y+i)*100, units = "px", dpi = 100 ) # 途中経過を表示 print(paste0(i, " (", round(i/frame_num*100, 1), "%)")) }
保存用のフォルダは空である必要があります。frame_num
枚の画像(グラフ)が保存されます。
動画の作成:加工処理なし
続いて、複数枚の画像ファイルをそのまま使って1つの動画ファイルにします。
ファイルパスを作成します。
# ファイル名を取得 file_name_vec <- list.files(dir_path) # ファイルパスを作成 file_path_vec <- paste0(dir_path, "/", file_name_vec) file_path_vec[1:5]
## [1] "tmp_folder/01.png" "tmp_folder/02.png" "tmp_folder/03.png" ## [4] "tmp_folder/04.png" "tmp_folder/05.png"
ファイル名の値が小さいほど、座標(サイズ)が小さいグラフ(画像)です。
サイズが小さい順に画像ファイルを読み込んで、gifファイルに変換します。
# 画像ファイルを読み込み pic_data_vec <- magick::image_read(path = file_path_vec) # gif画像を作成 gif_data <- magick::image_animate(image = pic_data_vec, fps = 1, dispose = "previous") # gifファイルを書き出し magick::image_write_gif(image = gif_data, path = "heatmap.gif", delay = 0.4)
image_read()
で画像を読み込んで、image_animate()
でgifファイルに変換して、image_write_gif()
で書き出します。
この方法でアニメーションを作成した場合、最初のサイズよりも大きい場合は、トリミングされ全てのフレームでサイズが統一されます。
image_animate()
で処理せずにgifファイルを作成します。
# 画像ファイルを読み込み pic_data_vec <- magick::image_read(path = file_path_vec) # gifファイルを書き出し magick::image_write_gif(image = pic_data_vec, path = "heatmap.gif", delay = 0.4)
## Images are not the same size. Resizing to 500x400...
こちらの方法では、全てのフレームが最初のサイズにリサイズされます。最初のサイズよりも大きい場合は小さく、小さい場合は大きくなります。リサイズの際にアスペクト比が変わることもあります。(アスペクト比によっては?リサイズされずにエラーになることもあった気がするのですが、再現できませんでした。)
今度は、サイズが大きい順に画像ファイルを読み込んで、gifファイルに変換します。
# gif画像を作成 file_path_vec |> rev() |> magick::image_read() |> magick::image_animate() |> magick::image_write_gif(path = "heatmap.gif", delay = 0.4)
rev()
でfile_path_vec
を逆順に並べ替えて画像を読み込み、image_animate()
を使ってgif画像を作成します。pic_data_vec
に対してrev()
を使っても処理できます。
最初のサイズよりも小さい場合は、透過背景?白背景?によって全てのフレームでサイズが統一されます。
image_animate()
を使わずにgifファイルを作成します。
# gif画像を作成 file_path_vec |> rev() |> magick::image_read() |> magick::image_write_gif(path = "heatmap.gif", delay = 0.4)
## Images are not the same size. Resizing to 1400x1300...
こちらは先ほどと同様に、全てのフレームが最初のサイズにリサイズされます。先ほどよりも最初のサイズが大きいので、全体サイズも大きくなっています。
動画の作成:加工処理あり
続いて、複数枚の画像ファイルに関して、サイズを統一する処理を行って1つの動画ファイルにします。
リサイズ
画像サイズを合わせるように変形してからアニメーションを作成します。サイズ変更については「magick::image_scale関数による画像変形 - からっぽのしょこ」を参照してください。
全ての画像の情報を取得します。
# 画像情報を取得 pic_info_df <- file_path_vec |> magick::image_read() |> magick::image_info() pic_info_df
## # A tibble: 10 × 7 ## format width height colorspace matte filesize density ## <chr> <int> <int> <chr> <lgl> <int> <chr> ## 1 PNG 500 400 sRGB FALSE 20452 39x39 ## 2 PNG 600 500 sRGB FALSE 29291 39x39 ## 3 PNG 700 600 sRGB FALSE 39958 39x39 ## 4 PNG 800 700 sRGB FALSE 52067 39x39 ## 5 PNG 900 800 sRGB FALSE 65956 39x39 ## 6 PNG 1000 900 sRGB FALSE 82704 39x39 ## 7 PNG 1100 1000 sRGB FALSE 98024 39x39 ## 8 PNG 1200 1100 sRGB FALSE 114962 39x39 ## 9 PNG 1300 1200 sRGB FALSE 135178 39x39 ## 10 PNG 1400 1300 sRGB FALSE 156548 39x39
image_info()
で画像サイズなどを得られます。
横サイズ(横方向ピクセル数)と縦サイズ(縦方向ピクセル数)の最大値を取得します。
# 縦横の最大サイズを取得 max_w <- max(pic_info_df[["width"]]) max_h <- max(pic_info_df[["height"]]) max_w; max_h
## [1] 1400 ## [1] 1300
リサイズ後の画像情報を確認しておきます。
# 最大サイズに変形して確認 file_path_vec |> magick::image_read() |> magick::image_scale(geometry = paste0(max_w, "x", max_h)) |> magick::image_info()
## # A tibble: 10 × 7 ## format width height colorspace matte filesize density ## <chr> <int> <int> <chr> <lgl> <int> <chr> ## 1 PNG 1400 1120 sRGB FALSE 0 39x39 ## 2 PNG 1400 1167 sRGB FALSE 0 39x39 ## 3 PNG 1400 1200 sRGB FALSE 0 39x39 ## 4 PNG 1400 1225 sRGB FALSE 0 39x39 ## 5 PNG 1400 1244 sRGB FALSE 0 39x39 ## 6 PNG 1400 1260 sRGB FALSE 0 39x39 ## 7 PNG 1400 1273 sRGB FALSE 0 39x39 ## 8 PNG 1400 1283 sRGB FALSE 0 39x39 ## 9 PNG 1400 1292 sRGB FALSE 0 39x39 ## 10 PNG 1400 1300 sRGB FALSE 0 39x39
image_scale()
で画像サイズを指定して変形できます。geometry
引数にサイズの上限を"横サイズx縦サイズ"
の形式で指定すると、それぞれの値を超えない範囲でアスペクト比を保って変形します。
アスペクト比が変わらないので、全ての画像が指定したサイズになるわけではありません。アスペクト比を保たずに指定したサイズにする場合は、"横サイズx縦サイズ!"
を指定します。
画像サイズを調整して、gifファイルを作成します。
# 最大サイズに変形してgif画像を作成 file_path_vec |> #rev() |> magick::image_read() |> magick::image_scale(geometry = paste0(max_w, "x", max_h)) |> #magick::image_animate(fps = 1, dispose = "previous") |> magick::image_write_gif(path = "heatmap.gif", delay = 0.4)
## Images are not the same size. Resizing to 1400x1120...
全ての画像を同じサイズにできていないので、image_write_gif()
によってアスペクトを保たないサイズに変形されています。image_animate()
を使う場合はトリミングされます。
背景画像を挿入
最大サイズの背景画像を用意しておき各画像を重ねることでサイズを統一してからアニメーションを作成します。
背景画像を読み込んで、最大サイズに変形します。
# 背景用の画像を読み込み background_data <- magick::image_read(path = "../data/picture/background.png") |> magick::image_scale(geometry = paste0(max_w, "x", max_h, "!")) background_data |> magick::image_info() background_data
## # A tibble: 1 × 7 ## format width height colorspace matte filesize density ## <chr> <int> <int> <chr> <lgl> <int> <chr> ## 1 PNG 1400 1300 sRGB FALSE 0 47x47
この例では真っ白の画像を使います。
1枚ずつ背景画像に重ねます(本当はベクトルのまま処理したかった)。
# 画像ごとに背景を挿入 for(i in seq_along(file_path_vec)) { # i番目のファイルパスを抽出 file_path <- file_path_vec[i] # 画像を読み込み pic_data <- magick::image_read(path = file_path) if(i == 1) { # 背景画像に重ねる pic_data_vec <- magick::image_mosaic(image = c(background_data, pic_data)) } else { # 背景画像に重ねる pic_back_data <- magick::image_mosaic(image = c(background_data, pic_data)) # ベクトルに格納 pic_data_vec <- c(pic_data_vec, pic_back_data) } } pic_data_vec[1]
画像データのベクトルをimage_mosaic()
に渡すと重ねた画像を作成します。
加工した画像データをpic_data_vec
に格納していきます。
gifファイルを作成します。
# gif画像を作成 pic_data_vec |> magick::image_animate(fps = 1, dispose = "previous") |> magick::image_write_gif(path = "heatmap.gif", delay = 0.4)
各画像が左上で固定されたアニメーションになります。
この記事では、magick
パッケージによるアニメーションの作成を解説しました。
参考
おわりに
上手くいかない例を載せると記事が冗長になりますね。もう少しパッケージの関数を見ていけばもっと上手く処理できそうな気もしますが、それはまた次の機会とします。
patchwork
を使うとgganimate
を使えないのが残念と思いつつ使えたらやりたいことがさらに増えて先に進めないしまぁいいかと思ってたのですが、うっかりmagick
を使ってグラフを動かせるようになってしまいました。
【次の内容】