楚新元 | All in R

Welcome to R Square

用 R 生成字符动画

楚新元 / 2021-08-18


获取动画

library(magick)
if (dir.exists("./data") == FALSE) dir.create("./data")
img_path = "https://picb.zhimg.com/v2-f77c2758553b63f51f2061fc711101e8_b.webp"
if (file.exists("./data/ball.webp") == FALSE) {
  download.file(img_path, "./data/ball.webp", mode = "wb")
}
img = image_read("./data/ball.webp")
img

动画简单处理

倒放动画

img_rev = rev(img)
img_rev

局部放慢

img_slow = img[c(1:30, rep(31:44, each = 3))]
img_slow

从局部放慢动画里提取每帧图片

if (dir.exists("./result") == FALSE) dir.create("./result")
for (i in 1:length(img_slow)) {
  image_write(img_slow[i], format = "png", paste0("./result/", i, ".png"))
}

每帧图片转为字符画

自定义函数

注:这个函数参考辉小宝同学知乎文章,详见:https://zhuanlan.zhihu.com/p/51153590

library(magick)
image2chars = function(
  pathIn = '',
  pathOutTxt = NULL,
  pathOutImg = NULL,
  jpg_quality = 80,
  width = 100,
  chars = c('&', '#', 'w', 's', 'k', 'd', 't', 'j', 'i', '.', ' '),
  isColor = FALSE
  ){

  # pathIn: 原始图片的路径,支持各种格式。
  # pathOutTxt: 字符文本的输出路径,默认与原始图片在同一文件夹下。
  # pathOutImg: 字符图片的输出路径,默认与原始图片在同一文件夹下。
  # jpg_quality: 字符图片的质量,范围0-100。
  # width: 字符文本的宽度,默认为100,即一行100个字符。
  # chars: 字符集,可自定义;
  # isColor: 字符图片是否为彩色,默认为黑白字符图片

  img = image_read(pathIn)  # 读入图片
  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)

  # 输出字符文本,并保存成文件
  if(is.null(pathOutTxt))
    pathOutTxt <- paste0(gsub(".png", "", pathIn), ".txt")  
  write.table(labels_mat, pathOutTxt, quote = F, row.names = F, col.names = F)


  # 绘制字符图片,给相应字符着色,并保存成文件
  if(isColor){
    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(pathOutImg))
    pathOutImg = paste0(gsub(".png", "", pathIn), ".jpg")  
  jpeg(pathOutImg, width = 16 * w, height = 16 * h, quality = jpg_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 (isColor) {
    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()
}

利用自定义函数实现图片转字符

for (i in 1:length(img_slow)) {
  pathIn = paste0("./result/", i, ".png")
  image2chars(
    pathIn, 
    width = 72, 
    isColor = TRUE, 
    chars = c("0", "1")
    )
}

将字符图片转化为动画

library(gganimate)
library(magick)

p = 1:length(img_slow)
animate_p = image_animate(
  image = image_read(path = paste0("./result/", p, ".jpg"))
  )
anim_save(
  filename = "ball_chars.gif", 
  animation = animate_p
  )

ball_chars = image_read("ball_chars.gif")
ball_chars