科研人员焦虑到头疼的时候,止痛靠艺术.gif
这个要看到最后啊。
最近啃下了图片处理的神包magick
, 里面把动图融合到另外一张图片中的操作很神奇。
举个简单的例子。
第一个例子
加载R包,用image_read
函数读入图片
install.packages("magick")
library(magick)
# 读取前景图片
banana <- image_read("https://jeroen.github.io/images/banana.gif")
可以用image_scale
函数调整图片大小
banana <- image_scale(banana, "150")
导入一张背景图片,调整大小,设置为背景
logo <- image_read("https://jeroen.github.io/images/Rlogo.png")
logo <- image_scale(logo, "200")
background <- image_background(logo, "white", flatten = TRUE)
现在通过image_composite
函数把两个图片整合起来,offset
参数设置图片的相对位置,第一个加号是x轴,第二个加号是y轴
frames <- image_composite(background, banana, offset = "+70+30")
用image_animate
函数这是最终图片的帧数快慢
image_animate(frames, fps = 10)
这时候,图片就做好了,好像并不惊艳,因为用PPT就可以实现这个操作。
第二个例子
下面我们就这个例子,再换种方法操作,这里面涉及到批量操作。
一样的读入两张图片,调整大小
library(magick)
logo <- image_read("https://www.r-project.org/logo/Rlogo.png")
banana <- image_read("https://jeroen.github.io/images/banana.gif")
## 调整图片大小
front <- image_scale(banana, "300")
background <- image_scale(logo, "400")
批量地把用image_flatten
函数,把动图的帧数一张张融合到背景上,形成多张图片,储存为列表
frames <- lapply(as.list(front), function(x) image_flatten(c(background, x)))
用image_join
函数把他们合并起来
image_join(frames)
最后再用image_animate
函数把他们动起来
image_animate(image_join(frames))
在这里我知道一个概念,就是image_join
函数可以操作列表。
第三个例子
下面举第三个例子,学习图片和ggplot2图表的融合。
先读入一张图片,调整大小
frink <- image_read("https://jeroen.github.io/images/frink.png")
frink <- image_scale(frink, "150")
下面这个操作,很不得了,ggplot2直接和图片不好融合,整理提供了一个方案,就是先打开一张画布,把ggplot2的图画在里面,成为能够被识别的对象。
用image_graph
打开一张画布,然后ggplot2作图,最后关闭这张画布,对象储存为fig
fig <- image_graph(width = 400, height = 400, res = 96)
ggplot2::qplot(mpg, wt, data = mtcars, colour = cyl)
dev.off()
现在我们用之前的image_composite
函数就可以把他们融合起来了
image_composite(fig, frink, offset = "+220+70")
当然,把动图插入到ggplot2也是很容易的事情。
frames <- image_composite(fig, banana, offset = "+30+50")
image_animate(frames, fps = 10)
这跟香蕉贱兮兮的哈,真欠揍。
总结一下,
- 两张图片在R语言里面是可以融合的,如果是动图用
image_animate
来调整。 - 一张动图可以被拆分为多个帧,可用
image_join
批量整合。 -
image_graph
打开画布画图可以把画在里面的图存为可操作的格式
现在重点来了!
我在网上找到一个碉堡的构图函数
phi <- (1 + sqrt(5)) / 2
golden_angle <- pi*(3-sqrt(5))
create_art <- function(n=1800,u=5,v=0,angle=golden_angle,colors="#ffffff",...){
my_colours <- colors
df <- tibble(
idx = c(0:(n-1)),
t = seq(0,2*pi,length.out=n),
r = sqrt(idx),
x = r*cos(angle*idx),
y = r*sin(angle*idx),
color_angle = atan2(y=y,x=x)
)
v <- ifelse(v<u,v,v%%u)
max_r <- max(df$r)*1.1
my_art <- df %>%
ggplot(aes(x=x,y=y,color=color_angle)) +
geom_path(data= . %>% filter(idx%%u==v),
lineend="round", linejoin="mitre", linemitre=3,
aes(size=idx, alpha=idx)) +
coord_fixed() +
theme_void() +
scale_alpha_continuous(guide="none", range=c(0,1), trans="sqrt")+
scale_size_continuous(guide="none", range=c(10,0), trans="sqrt") +
scale_color_gradientn(guide="none", colors=my_colours) +
theme(panel.background = element_rect(fill="#000000de")) +
expand_limits(x=c(-max_r,max_r),y=c(-max_r,max_r))
my_art + annotate(geom="text", x=Inf,y=-Inf,
label=str_glue('n: {n} | u: {u} | v: {v} | angle: {round(angle,3)} radian'),
family="Roboto Condensed", color="#ffffffae",
hjust=1,vjust=-1)
}
这个create_art
函数可以画出这样的图
library(tibble)
library(dplyr)
library(ggplot2)
library(stringr)
create_art()
更厉害的是,这个函数有四个重要的参数,大概是这样的。
n, 点的数量
u,跳过固定的点
angle,点之间的角度
v,整体图片的角度
我们用patchwork
连接多个图来集中展示
devtools::install_github("thomasp85/patchwork")
library(patchwork)
现在仅仅改变点和点之间的角度来作8张图
create_art(n=360,angle=sqrt(2)) +
create_art(n=360,angle=sqrt(3)) +
create_art(n=360,angle=sqrt(5)) +
create_art(n=360,angle=pi/7) +
create_art(n=360,angle=pi/9) +
create_art(n=360,angle=pi/46) +
create_art(n=360,angle=2) +
create_art(n=360,angle=1) +
plot_layout(ncol = 4)
很惊艳是吧。
现在仅仅改变跳过的点数这个参数来做8张图
create_art(n=360,u=5) +
create_art(n=360,u=11) +
create_art(n=360,u=13) +
create_art(n=360,u=6) +
create_art(n=360,u=9) +
create_art(n=360,u=8) +
create_art(n=360,u=17) +
create_art(n=360,u=3) +
plot_layout(ncol = 4)
不可以思议。
那么现在问题来了,v这个选择整体图片角度的参数还没有用呢,如果我们制作成一系列带不同角度的图片,再用刚才学到的批量技能把他们融合起来,岂不是能够创造炫酷的动图?
首先,image_graph
打开画布
其次,制作一个数据框,把几个参数一行行表示,这里面改变的只有v
使用pmap
函数批量操作,这个函数很神奇,你如果传给他一个数据框,他就把一行一行的数据当做参数传给后面的函数
最后,关闭图形设备,所有的图片以列表形式存在imgs_1中。
library(magick)
imgs_1 <- image_graph(width=600, height=600)
params <- tibble(n=1800,u=44,v=seq(0,u-1,by=2),angle=golden_angle)
purrr::pmap(params,create_art)
dev.off()
这时候再用image_animate
函数把她动起来。
image_animate(imgs_1,fps=10)
这真是思路决定出路
换一个图更加炫酷
imgs_2 <- image_graph(width=600, height=600)
params <- tibble(n=1800,u=22,v=seq(0,u-1,by=1),angle=-golden_angle)
purrr::pmap(params,create_art)
dev.off()
image_animate(imgs_2,fps=10)
好了,我们已经理解了他做动图的逻辑,就是把一系列图片合在一起,按照一定速度一张张播放,最终产生gif。
制作gif
如果这个理解是对的话,我们就有了一个制作gif动图的神器。
比如我在PPT里面先创作几张图片导出来,放在当前工作目录的magick文件夹中
然后批量读入,联合,加速播放即可
library(magick)
dd <- lapply(paste0("./magick/",list.files("./magick",pattern = "*.jpg")),image_read)
image_animate(image_join(dd),fps=4)
服不服?
理论上,如果你给他足够多的依次发生的图片,什么动作小人书,小电影都没问题,以下我简单地示范一下。
本次参考(甚至是复制)的教程在这里:
magick
:
https://cran.r-project.org/web/packages/magick/vignettes/intro.html#installing_magick
create_art
:
https://chichacha.netlify.com/2019/01/29/playing-around-with-phyllotactic-spirals/
好了,我是果子,这几天很焦虑,明天见。