用 R 生成字符动画
楚新元 / 2021-08-18
加载相关 R 包
library(magick)
library(purrr)
library(gganimate)
获取动画
img = image_read("./data/ball.gif")
img
动画简单处理
倒放动画
img_rev = rev(img)
img_rev
局部放慢
img_slow = img[c(1:30, rep(31:44, each = 3))]
img_slow
从局部放慢动画里提取每帧图片
if (!dir.exists("result1")) dir.create("result1")
path1 = "./result1"
1:length(img_slow) %>%
map(
\(x) image_write(
img_slow[x],
format = "png",
paste0(path1, "/", x, ".png")
)
)
每帧图片转为字符画
注:这个函数参考辉小宝同学知乎文章,详见:https://zhuanlan.zhihu.com/p/51153590
# 编写图片转为字符画的函数
convert_chars = \(
input, outdir = NULL, quality = 80,
width = 100, is.color = FALSE,
chars = c('&', '#', 'i', '.', '*')
) {
# input: 原始图片的路径,支持 magick 包支持的各种格式。
# outdir:默认输出路径和输入图片路径相同,可自定义。
# quality: 字符图片的质量,范围 0-100。
# width: 字符文本的宽度,默认为 100,即一行 100 个字符。
# is.color: 字符图片是否为彩色,默认为黑白字符图片。
# chars: 字符集,可自定义。
# 读入图片
img = image_read(input)
gray = image_convert(img, colorspace = 'gray') # 转为灰度图
rgb = image_convert(img, colorspace = 'rgb') # 转为 rgb 图
# 修改图片尺寸
gray = image_resize(gray, paste0(width, 'x'))
rgb = image_resize(rgb, paste0(width, 'x'))
# 获取图片灰度值矩阵,并将各像素值对应于相应字符
gray = as.integer(image_data(gray))[, , 1]
w = ncol(gray) # 图片宽度
h = nrow(gray) # 图片高度
index = findInterval(
c(gray),
seq(0, 255, length.out = length(chars) + 1),
rightmost.closed = T
)
labels = chars[index]
labels_mat = matrix(labels, ncol = w)
# 确定 txt 文件输出路径
input_ext = tools::file_ext(input)
if (is.null(outdir)) {
dest_txt = gsub(input_ext, "txt", input)
} else {
dest_txt = file.path(
outdir,
paste0(gsub(".*/(.*?)\\..*", "\\1", input), ".txt")
)
}
# 输出字符到 txt 文件内并保存到指定路径下
write.table(
labels_mat, dest_txt, quote = FALSE,
row.names = FALSE, col.names = FALSE
)
# 绘制字符图片,给相应字符着色,并保存成文件
if (is.color) {
rgb = as.integer(image_data(rgb))
r = rgb[, , 1] # red通道像素矩阵
g = rgb[, , 2] # green通道像素矩阵
b = rgb[, , 3] # blue通道像素矩阵
cols = rgb(c(r), c(g), c(b), maxColorValue = 255) # 转化为颜色表示
}
if (is.null(outdir)) {
dest_img = gsub(input_ext, "jpg", input)
} else {
dest_img = file.path(
outdir,
paste0(gsub(".*/(.*?)\\..*", "\\1", input), ".jpg")
)
}
# 生成 .jpg 格式图片并保存到指定路径下
jpeg(dest_img, width = 16 * w, height = 16 * h, quality = quality)
op = par(mar = c(0, 0, 0, 0))
plot(
0, xlab = '', ylab = '', asp = 1,
xlim = c(0, w), ylim = c(0, h),
xaxs = "i", yaxs = "i",
type = 'n', axes = FALSE
)
grid = expand.grid(y = h:1 - 0.5, x = 1:w - 0.5) # 各字符位置
if (is.color) {
text(grid$x, grid$y, labels, cex = 1.5, col = cols) # 绘制彩色字符
} else {
text(grid$x, grid$y, labels, cex = 1.5) # 绘制黑白字符
}
par(op)
dev.off()
}
利用自定义函数实现图片转字符图片
if (!dir.exists("result2")) dir.create("result2")
path2 = "./result2"
path1 %>%
list.files(full.names = TRUE) %>%
walk(
\(x) convert_chars(
input = x,
outdir = path2,
width = 72,
is.color = TRUE,
chars = c("0", "1")
)
)
将字符图片转化为动画
# 生成动画
path2 %>%
list.files(
pattern = "\\.jpg$",
full.names = TRUE
) %>%
image_read() %>%
image_animate() -> anim_obj
anim_obj
# 保存动画
anim_save(
animation = anim_obj,
filename = "ball_chars.gif"
)