Python 中有很多不错的数据可视化库,但是极少能渲染 GIF 图或视频动画效果。本文就分享一下如何用 MoviePy 作为其他可视化库的通用插件,制作动画可视化效果,毕竟这年头,没图不行,有动图更好。

MoviePy 能让我们用函数 make_frame(t) 自定义动画,函数会返回和时间 t 的视频帧(以秒为单位):

from moviepy.editor import VideoClip

def make_frame(t):

""" returns an image of the frame at time t """

# ... 用任意库创建帧

return frame_for_time_t # (Height x Width x 3) Numpy array

animation = VideoClip(make_frame, duration=3) # 3-second clip

# 支持导出为多种格式

animation.write_videofile("my_animation.mp4", fps=24) # 导出为视频

animation.write_gif("my_animation.gif", fps=24) # 导出为GIF

本文会涵盖 MayaVi、vispy、matplotlib、NumPy 和 Scikit-image 这些库。

基于 Mayavi 制作动画

Mayavi 是一个 Python 模块,可以制作交互式 3D 数据可视化。在第一个例子中,我们会将一个高度随着时间 t 不断变化的表面制作成动画:

import numpy as np

import mayavi.mlab as mlab

import moviepy.editor as mpy

duration= 2 # duration of the animation in seconds (it will loop)

# 用Mayavi制作一个图形

fig_myv = mlab.figure(size=(220,220), bgcolor=(1,1,1))

X, Y = np.linspace(-2,2,200), np.linspace(-2,2,200)

XX, YY = np.meshgrid(X,Y)

ZZ = lambda d: np.sinc(XX**2+YY**2)+np.sin(XX+d)

# 用MoviePy将图形转换为动画,编写动画GIF

def make_frame(t):

mlab.clf() # 清掉图形(重设颜色)

mlab.mesh(YY,XX,ZZ(2*np.pi*t/duration), figure=fig_myv)

return mlab.screenshot(antialiased=True)

animation = mpy.VideoClip(make_frame, duration=duration)

animation.write_gif("sinc.gif", fps=20)

另外一个例子是,制作一个坐标和观看角度都随着时间不断变化的线框网动画:

import numpy as np

import mayavi.mlab as mlab

import moviepy.editor as mpy

duration = 2 # duration of the animation in seconds (it will loop)

# 用Mayavi制作一个图形

fig = mlab.figure(size=(500, 500), bgcolor=(1,1,1))

u = np.linspace(0,2*np.pi,100)

xx,yy,zz = np.cos(u), np.sin(3*u), np.sin(u) # 点

l = mlab.plot3d(xx,yy,zz, representation="wireframe", tube_sides=5,

line_width=.5, tube_radius=0.2, figure=fig)

# 用MoviePy将图形转换为动画,编写动画GIF

def make_frame(t):

""" Generates and returns the frame for time t. """

y = np.sin(3*u)*(0.2+0.5*np.cos(2*np.pi*t/duration))

l.mlab_source.set(y = y) # change y-coordinates of the mesh

mlab.view(azimuth= 360*t/duration, distance=9) # 相机视角

return mlab.screenshot(antialiased=True) # 返回RGB图形

animation = mpy.VideoClip(make_frame, duration=duration).resize(0.5)

# 视频生成花费10秒, GIF 生成花费25秒

animation.write_videofile("wireframe.mp4", fps=20)

animation.write_gif("wireframe.gif", fps=20)

基于 Vispy 制作动画

Vispy 是另一款基于 OpenGL 的交互式 3D 数据可视化库。我们可以先用 Vispy 做出图形和网格,然后用 MoviePy 将其制作成动画:

from moviepy.editor import VideoClip

import numpy as np

from vispy import app, scene

from vispy.gloo.util import _screenshot

canvas = scene.SceneCanvas(keys='interactive')

view = canvas.central_widget.add_view()

view.set_camera('turntable', mode='perspective', up='z', distance=2,

azimuth=30., elevation=65.)

xx, yy = np.arange(-1,1,.02),np.arange(-1,1,.02)

X,Y = np.meshgrid(xx,yy)

R = np.sqrt(X**2+Y**2)

Z = lambda t : 0.1*np.sin(10*R-2*np.pi*t)

surface = scene.visuals.SurfacePlot(x= xx-0.1, y=yy+0.2, z= Z(0),

shading='smooth', color=(0.5, 0.5, 1, 1))

view.add(surface)

canvas.show()

# 用MoviePy转换为动画

def make_frame(t):

surface.set_data(z = Z(t)) # 更新曲面

canvas.on_draw(None) # 更新Vispy的画布上的 图形

return _screenshot((0,0,canvas.size[0],canvas.size[1]))[:,:,:3]

animation = VideoClip(make_frame, duration=1).resize(width=350)

animation.write_gif('sinc_vispy.gif', fps=20, opt='OptimizePlus')

下面是一些用 Vispy 制作的更复杂点的酷炫动画,它们是将 C 语言代码片段嵌入 Python 代码中,并微调 3D 着色器后制作而成:

基于 matplotlib 制作动画

虽然 2D/3D 绘图库 matplotlib 内置了动画模块,但是用 MoviePy 制作更轻更高质量的视频动画,而且运行速度更快。下面是用 MoviePy 基于 matplotlib 制作动画的方法:

import matplotlib.pyplot as plt

import numpy as np

from moviepy.video.io.bindings import mplfig_to_npimage

import moviepy.editor as mpy

# 用matplotlib绘制一个图形

duration = 2

fig_mpl, ax = plt.subplots(1,figsize=(5,3), facecolor='white')

xx = np.linspace(-2,2,200) # x向量

zz = lambda d: np.sinc(xx**2)+np.sin(xx+d) # (变化的)Z向量

ax.set_title("Elevation in y=0")

ax.set_ylim(-1.5,2.5)

line, = ax.plot(xx, zz(0), lw=3)

# 用MoviePy制作动(为每个t更新曲面)。制作一个GIF

def make_frame_mpl(t):

line.set_ydata( zz(2*np.pi*t/duration)) # 更新曲面

return mplfig_to_npimage(fig_mpl) # 图形的RGB图像

animation =mpy.VideoClip(make_frame_mpl, duration=duration)

animation.write_gif("sinc_mpl.gif", fps=20)

Matplotlib 有很多漂亮的主题,和 Pandas、Scikit-Learn 等数字模块的兼容性也很好。我们来看一个 SVM 分类器,更好的理解随着训练点的数量增加时地图的变化动态:

import numpy as np

import matplotlib.pyplot as plt

from sklearn import svm # sklearn = scikit-learn

from sklearn.datasets import make_moons

from moviepy.editor import VideoClip

from moviepy.video.io.bindings import mplfig_to_npimage

X, Y = make_moons(50, noise=0.1, random_state=2) # 半随机数据

fig, ax = plt.subplots(1, figsize=(4, 4), facecolor=(1,1,1))

fig.subplots_adjust(left=0, right=1, bottom=0)

xx, yy = np.meshgrid(np.linspace(-2,3,500), np.linspace(-1,2,500))

def make_frame(t):

ax.clear()

ax.axis('off')

ax.set_title("SVC classification", fontsize=16)

classifier = svm.SVC(gamma=2, C=1)

# 不断变化的权重让数据点一个接一个的出现

weights = np.minimum(1, np.maximum(0, t**2+10-np.arange(50)))

classifier.fit(X, Y, sample_weight=weights)

Z = classifier.decision_function(np.c_[xx.ravel(), yy.ravel()])

Z = Z.reshape(xx.shape)

ax.contourf(xx, yy, Z, cmap=plt.cm.bone, alpha=0.8,

vmin=-2.5, vmax=2.5, levels=np.linspace(-2,2,20))

ax.scatter(X[:,0], X[:,1], c=Y, s=50*weights, cmap=plt.cm.bone)

return mplfig_to_npimage(fig)

animation = VideoClip(make_frame, duration = 7)

animation.write_gif("svm.gif", fps=15)

简单来说,通过背景颜色我们就可以得知分类器辨识黑色点和白色点属于哪里。刚开始并不明显,但随着越来越多的数据点出现,这些点的分布逐渐呈月牙形区域。

基于 Numpy 的动画

如果是用 Numpy 数组(Numpy 是 Python 中的一个数字库),你不需要任何外部绘图库,你可以直接将数组输入 MoviePy 里。

将 Numpy 和 MoviePy 结合,可以做出很炫酷的动画效果。比如我们可以模拟僵尸病毒在法国蔓延的动态图(模拟!模拟!),以网格形式(Numpy 数组)模拟出法国地图,在上面执行所有模拟病毒感染和扩散效果的计算。每隔一段时间,一些 Numpy 操作会将网格转换为有效的 RGB 图像,并将其发送至 MoviePy:

import urllib

import numpy as np

from scipy.ndimage.filters import convolve

import moviepy.editor as mpy

#### 从网络上检索地图

filename = ("http://upload.wikimedia.org/wikipedia/commons/a/aa/"

"France_-_2011_population_density_-_200_m_%C3%"

"97_200_m_square_grid_-_Dark.png")

urllib.urlretrieve(filename, "france_density.png")

#### 参数和约束条件

infection_rate = 0.3

incubation_rate = 0.1

dispersion_rates = [0, 0.07, 0.03] # for S, I, R

# 该内核会模拟人类/僵尸如何用一个位置扩散至邻近位置

dispersion_kernel = np.array([[0.5, 1 , 0.5],

[1 , -6, 1],

[0.5, 1, 0.5]])

france = mpy.ImageClip("france_density.png").resize(width=400)

SIR = np.zeros( (3,france.h, france.w), dtype=float)

SIR[0] = france.get_frame(0).mean(axis=2)/255

start = int(0.6*france.h), int(0.737*france.w)

SIR[1,start[0], start[1]] = 0.8 # infection in Grenoble at t=0

dt = 1.0 # 一次更新=实时1个小时

hours_per_second= 7*24 # one second in the video = one week in the model

world = {'SIR':SIR, 't':0}

##### 建模

def infection(SIR, infection_rate, incubation_rate):

""" Computes the evolution of #Sane, #Infected, #Rampaging"""

S,I,R = SIR

newly_infected = infection_rate*R*S

newly_rampaging = incubation_rate*I

dS = - newly_infected

dI = newly_infected - newly_rampaging

dR = newly_rampaging

return np.array([dS, dI, dR])

def dispersion(SIR, dispersion_kernel, dispersion_rates):

""" Computes the dispersion (spread) of people """

return np.array( [convolve(e, dispersion_kernel, cval=0)*r

for (e,r) in zip(SIR, dispersion_rates)])

def update(world):

""" spread the epidemic for one time step """

infect = infection(world['SIR'], infection_rate, incubation_rate)

disperse = dispersion(world['SIR'], dispersion_kernel, dispersion_rates)

world['SIR'] += dt*( infect + disperse)

world['t'] += dt

# 用MoviePy制作动画

def world_to_npimage(world):

""" Converts the world's map into a RGB image for the final video."""

coefs = np.array([2,25,25]).reshape((3,1,1))

accentuated_world = 255*coefs*world['SIR']

image = accentuated_world[::-1].swapaxes(0,2).swapaxes(0,1)

return np.minimum(255, image)

def make_frame(t):

""" Return the frame for time t """

while world['t'] < hours_per_second*t:

update(world)

return world_to_npimage(world)

animation = mpy.VideoClip(make_frame, duration=25)

# 可以将结果写为视频或GIF(速度较慢)

#animation.write_gif(make_frame, fps=15)

animation.write_videofile('test.mp4', fps=20)

最终效果如下:

将动画组合到一起

如果一个动画不够好看,那就来两个!我们可以借助 MoviePy 的视频组合功能将来自不同库的动画组合在一起:

import moviepy.editor as mpy

# 我们使用之前生成的GIF图以避免重新计算动画

clip_mayavi = mpy.VideoFileClip("sinc.gif")

clip_mpl = mpy.VideoFileClip("sinc_mpl.gif").resize(height=clip_mayavi.h)

animation = mpy.clips_array([[clip_mpl, clip_mayavi]])

animation.write_gif("sinc_plot.gif", fps=20)

或者更有艺术气息一点:

# 在in clip_mayavi中将白色变为透明

clip_mayavi2 = (clip_mayavi.fx( mpy.vfx.mask_color, [255,255,255])

.set_opacity(.4) # whole clip is semi-transparent

.resize(height=0.85*clip_mpl.h)

.set_pos('center'))

animation = mpy.CompositeVideoClip([clip_mpl, clip_mayavi2])

animation.write_gif("sinc_plot2.gif", fps=20)

我们也可以对动画注释,这点在比较不同的算法和过滤器时,非常有用。我们展示一下来自 Scikit-image 库中的四张变换图像:

import moviepy.editor as mpy

import skimage.exposure as ske # 改变尺度,直方图

import skimage.filter as skf # 高斯模糊

clip = mpy.VideoFileClip("sinc.gif")

gray = clip.fx(mpy.vfx.blackwhite).to_mask()

def apply_effect(effect, title, **kw):

""" Returns a clip with the effect applied and a title"""

filtr = lambda im: effect(im, **kw)

new_clip = gray.fl_image(filtr).to_RGB()

txt = (mpy.TextClip(title, font="Purisa-Bold", fontsize=15)

.set_position(("center","top"))

.set_duration(clip.duration))

return mpy.CompositeVideoClip([new_clip,txt])

# 为原始动画应用4种不同的效果

equalized = apply_effect(ske.equalize_hist, "Equalized")

rescaled = apply_effect(ske.rescale_intensity, "Rescaled")

adjusted = apply_effect(ske.adjust_log, "Adjusted")

blurred = apply_effect(skf.gaussian_filter, "Blurred", sigma=4)

# 将片段一起放在2 X 2的网格上,写入一个文件

finalclip = mpy.clips_array([[ equalized, adjusted ],

[ blurred, rescaled ]])

final_clip.write_gif("test2x2.gif", fps=20)

如果我们用 concatenate_videoclips 代替 CompositeVideoClip 和 clips_array,会得到标题效果式的动画:

import moviepy.editor as mpy

import skimage.exposure as ske

import skimage.filter as skf

clip = mpy.VideoFileClip("sinc.gif")

gray = clip.fx(mpy.vfx.blackwhite).to_mask()

def apply_effect(effect, label, **kw):

""" Returns a clip with the effect applied and a top label"""

filtr = lambda im: effect(im, **kw)

new_clip = gray.fl_image(filtr).to_RGB()

txt = (mpy.TextClip(label, font="Amiri-Bold", fontsize=25,

bg_color='white', size=new_clip.size)

.set_position(("center"))

.set_duration(1))

return mpy.concatenate_videoclips([txt, new_clip])

equalized = apply_effect(ske.equalize_hist, "Equalized")

rescaled = apply_effect(ske.rescale_intensity, "Rescaled")

adjusted = apply_effect(ske.adjust_log, "Adjusted")

blurred = apply_effect(skf.gaussian_filter, "Blurred", sigma=4)

clips = [equalized, adjusted, blurred, rescaled]

animation = mpy.concatenate_videoclips(clips)

animation.write_gif("sinc_cat.gif", fps=15)

结语

希望本文能帮你制作出令人惊艳的动画可视化。借助 MoviePy,也能将其它库的可视化转换为动画,只要其输出能转换成 Numpy 数组。

有些库本身也有动画模块,但通常修正和维护起来比较痛苦,MoviePy 相对稳定的多,也可以适用于很多情况。

另外,另一个 Python 库 ImageIO 也能编写视频,可以提供一个很简单的接口来读取或写入任何种类的图像、视频和容积数据。比如你可以用 imwrite() 写图像,用 mimwrite() 写视频/ GIF,用 volwrite() 写体积数据,或只是用 write() 写流式数据。

快去动手操作吧,GIF 万岁!

python制作简单动画_把数据摇起来!用Python制作动画可视化效果!相关推荐

  1. 用python进行简单的数据分析和数据可视化

    用python进行简单的数据分析和数据可视化 本篇文章主要是初步探索数据分析,简单了解数据分析大致流程 数据来源:来自于Kaggle平台上的一个项目:Explore San Francisco cit ...

  2. R语言使用hexbin包的hexbin函数可视化散点图、应对数据量太大、且有数据重叠的情况、普通散点图可视化效果变差的情况、提供了对六边形单元格的二元绑定、通过图例颜色标定每一个区域数据点的数量

    R语言使用hexbin包的hexbin函数可视化散点图.应对数据量太大.且有数据重叠的情况.普通散点图可视化效果变差的情况.hexbin函数提供了对六边形单元格的二元绑定.通过图例颜色标定每一个区域数 ...

  3. python制作简单动画_让数据动起来!用python制作动画可视化效果,让数据不再枯燥!...

    MoviePy允许我们自定义的动画功能make_frame (t).函数将返回视频帧时间t(以秒为单位):根据Mayavi Mayavi做出动画是一个Python模块,可以使交互式3 d数据可视化.在 ...

  4. python 制作简单网站_新手小白 做python爬虫 爬什么网站比较简单?

    本篇文章就新手小白来说,教大家怎么爬虫.现学现卖,看完再自己操作操作就会了~我就是这么学的,分享给想用python爬虫的小伙伴: 放个懒人目录:网络爬虫的行径 URL初步的概念 python与urll ...

  5. python动画定义篮球大小_篮球数据API接口 - 【篮球比赛动画直播变化数据】API调用示例代码...

    篮球比赛动画直播变化数据API调用示例,详细请查看在线文档,需注册下 package com.huaying.demo.basketball; import javax.xml.bind.JAXBCo ...

  6. 零基础python入门书籍推荐书目_清华大学出版社-图书详情-《Python数据科学零基础一本通》...

    序 多次与教育界的朋友相聚,谈到计算机语言的发展趋势时,大家一致认为 Python 是 当今最重要的计算机语言.许多知名公司,例如 Google.Facebook 等皆已将 Python 列 为必备计 ...

  7. 用python编写密码安全性_用大数据python保护密码

    本篇文章给大家带来的就是关于用大数据Python保护密码,大数据时代,各种网站都需要你注册账号,使用密码.往往大家为了省事,所有的平台的账号密码是一样的,假如某个网站数据泄露后,那你的隐私安全就有问题 ...

  8. python绘制八角图形_国内数据可视化公司

    数据是非常强大的.当然,如果你能真正理解它想告诉你的内容,那它的强大之处就更能体现出来了. 通过观察数字和统计数据的转换以获得清晰的结论并不是一件容易的事.必须用一个合乎逻辑的.易于理解的方式来呈现数 ...

  9. hbuilderx制作简单网页_网页制作的基本步骤是怎样的?制作简单网页的具体操作有哪些呢?...

    网页制作的基本步骤是怎样的?制作简单网页的具体操作有哪些呢?如果是详细的网页制作,设计和制作一样关键.有可能是网页设计制作新手对简单网页制作设计的具体操作不太清楚.更别说设计详细的网页了.下面一起来看 ...

最新文章

  1. 4.1ASP.NET Core请求过程「深入浅出ASP.NET Core系列」
  2. easyUI添加行操作
  3. 【codeforces 242E】XOR on Segment
  4. css文字向右对齐_web前端入门到实战:css常用样式对文本的处理演练
  5. Angular export class AppComponent里定义变量的实现原理
  6. center.php,mycenter.php
  7. 张文宏直播再曝金句:我宁可戴口罩开空调
  8. JAVA基于权重的抽奖
  9. 面向对象的javascript-引用、作用域、闭包、上下文--(1)
  10. 将字符串或数字倒序输出
  11. mft文件记录属性头包括_学懂主流NTFS分区文件系统,你也可以成为MM眼中的大神!...
  12. Python之ASCII码
  13. 十天学会php之第九天
  14. linux aria2 使用教程,Aria2使用详细教程
  15. 一款简单易用的web报表工具
  16. Android打地鼠小游戏案例
  17. RevitAPI: 当前视图为透视图的时候IdlingEvent不会被触发
  18. 微信小程序 获取组件数据
  19. 三阶线性自抗扰控制matlab实现
  20. 腾讯云创建辅助网卡实现源进源出

热门文章

  1. App Store Review Guidelines中文版-上部
  2. LaTeX论文图片排版
  3. 数据集划分、label生成及按label将图片分类到不同文件夹
  4. Shiro解决多个二级域名的单点登录问题
  5. 大数据与Hadoop系列之分布式文件系统(一)
  6. python使用opencv如何保存图片_OpenCV Python 保存图片
  7. PayPal开发文档整理(8)——PayPal支付产品和解决方案
  8. JAVA音视频解决方案----视频基础知识
  9. 自组织是管理者和成员的双向奔赴
  10. 案例一: 使用IDA PRO+OllyDbg+PEview 追踪windows API 动态链接库函数的调用过程。 首先用文本编辑器写一个C++源程序名为StackFrame.cpp ,代码如下: