基于Python和MoviePy庫實現數據的動態展示

基于Python和MoviePy庫實現數據的動態展示

(翻譯:以馬內利) 

原文鏈接:Data Animations With Python and MoviePy  


Python擁有很多實現數據可視化的庫,但是很少可以展示GIFs的動態視圖。 這篇博客主要介紹怎樣使用MoviePy庫作為一個其他可視化庫的通用插件。

MoviePy 提供函數 make_frame(t) 來創建相對于時間 t(s) 動畫視頻框。

from moviepy.editor import VideoClip

def make_frame(t):
    """ returns an image of the frame at time t """
    # ... create the frame with any library
    return frame_for_time_t # (Height x Width x 3) Numpy array

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

# For the export, many options/formats/optimizations are supported
animation.write_videofile("my_animation.mp4", fps=24) # export as video
animation.write_gif("my_animation.gif", fps=24) # export as GIF (slow)

在之前的博客中,曾把這個函數用于向量動畫(animate vector graphics, 基于Gizh庫), 還有光纖追蹤場景動畫(ray-traced 3D scenes, 基于POV-Ray). 這篇博客主要介紹科學計算先關庫, Mayavi, Vispy, Matplotlib還有 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把這個圖片創建為一個動畫,并保存

def make_frame(t):
    mlab.clf() # clear the figure (to reset the colors)
    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)

基于Python和MoviePy庫實現數據的動態展示

另一個例子將展示網眼線框的坐標和視角隨著時間的變化而變化。 

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)

# 先創建一個圖片

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) # Points
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) # camera angle
    return mlab.screenshot(antialiased=True) # return a RGB image

animation = mpy.VideoClip(make_frame, duration=duration).resize(0.5)
# Video generation takes 10 seconds, GIF generation takes 25s
animation.write_videofile("wireframe.mp4", fps=20)
animation.write_gif("wireframe.gif", fps=20)

基于Python和MoviePy庫實現數據的動態展示

因為Mayavi依賴于強大的ITK可視化引擎,因此它可以處理更加復雜的數據集。 

下面這個動畫來是一個Mayavi 的事例。 

代碼鏈接

EJZELfi.gif


基于Vispy的動畫

Vispy是另一個可交互動態3D動畫可視化庫,基于OpenGL. 對于Mayavi, 我們首先創建圖和網格,然后使用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()

# ANIMATE WITH MOVIEPY

def make_frame(t):
    surface.set_data(z = Z(t)) # Update the mathematical surface
    canvas.on_draw(None) # Update the image on Vispy's canvas
    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')

基于Python和MoviePy庫實現數據的動態展示

下面是一個來源于Vispy gallery的更加高級的例子,C嵌入到python里,用于調整優化3D的著色

代碼鏈接

6PNEYB9.gif

代碼鏈接

基于Python和MoviePy庫實現數據的動態展示


基于Matplotlib庫創建動畫

3D/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) # the x vector
zz = lambda d: np.sinc(xx**2)+np.sin(xx+d) # the (changing) z vector
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))  # <= Update the curve
    return mplfig_to_npimage(fig_mpl) # RGB image of the figure

animation =mpy.VideoClip(make_frame_mpl, duration=duration)
animation.write_gif("sinc_mpl.gif", fps=20)

基于Python和MoviePy庫實現數據的動態展示

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) # semi-random data

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)
    # the varying weights make the points appear one after the other
    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)

Matplotlib 擁有很多漂亮的框架,而且和很多數量模塊例如 Pandas 和 Scikit-Learn 兼容性良. 讓我們欣賞一下當訓練數據集增長的時候SVM分類器逐漸提高對于地圖分布的理解。 

基于Python和MoviePy庫實現數據的動態展示

簡單來說,背景顏色告訴我們黑點和白點屬于的類別。剛開始的時候沒有清晰地線索,隨著越來越多的數據加入以后,分類器逐漸理解數據呈月牙形分布狀態。  


使用Numpy制作動畫

如果經常處理Numpy數據陣列 (Numpy 是python的一個核心數字計算庫), 無需任何其他拓展作圖庫就可以把數據陣列直接輸入MoviePy.

下面的動畫展示了法國喪尸大爆發的仿真動畫(借鑒 Max Berggren的播客).  法國地圖由網格建模(使用Numpy array)。喪尸病毒傳播和感染都在這個模型上展示。 通過Numpy的一些簡單操作后,這些網格被直接轉化為

RGB圖片,并輸入到MoviePy. 

代碼鏈接

基于Python和MoviePy庫實現數據的動態展示


疊合動畫 

啥比一個動畫更炫酷呢? 哥們你答對了,兩個動畫!得益于MoviePy的合并功能,我們可以把兩個不同庫制作的動畫合并起來。 

import moviepy.editor as mpy
# We use the GIFs generated earlier to avoid recomputing the animations.
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)

基于Python和MoviePy庫實現數據的動態展示

或者來點更加文藝的

# 在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)

基于Python和MoviePy庫實現數據的動態展示

是不是有點太酷炫了,不過有時候觀眾們需要點重口味的東西才好。 

同樣我們也可以注釋動畫,從而實現比較不同過濾器或者算法。讓我們看一個Scikit-image庫的圖像轉換例子。 

import moviepy.editor as mpy
import skimage.exposure as ske # rescaling, histogram eq.
import skimage.filter as skf # gaussian blur

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])

# 在原動畫上實現四種效果
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)

# 把得到的四種效果圖合并在2x2分布的網格上,并寫入文件
finalclip = mpy.clips_array([[ equalized, adjusted ],
                             [ blurred,   rescaled ]])
final_clip.write_gif("test2x2.gif", fps=20)

cMoPY1d.gif

如果我們使用concatenate_videoclips來代替CompositeVideo 和 Clipandclips_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)

基于Python和MoviePy庫實現數據的動態展示

最后,像第一個例子一樣,MoviePy在處理視頻數據的時候非常實用。 最后一個例子為細菌種群在視頻范圍內的增長和在黑白像素下的計數。第三個動畫是細菌種群大小和時間的指數增長關系

代碼鏈接

uoITKiA.gif


一個庫就可以搞定所有動畫嗎? 

我希望我已經提供足夠多屌炸天的模板供你用來在組會報告的時候驚嚇你的同事了。任何庫只要輸出結果可以轉換成為Numpy陣列,就可以使用MoviePy做成動畫。 

一些作圖庫雖然已經提供了動畫模塊,但是修改和維護是件虐心虐身的事情。多虧了廣大用戶在不同環境下的測試工作,MoviePy已經逐漸趨于穩定(也許用戶已經懶得報告bug了), 可以適用于很多場景。雖然要走的路還很長,但是用戶逐漸青睞MoviePy用于展示視頻或者GIF動畫是一件樂事。就像用戶青睞Matplotlib為Pandas和Scikit-learn做可視化一樣。 

作為結尾福利,也是為了更好地符合你們的需要,我要向大家提一提另一個庫,ImageIO。 這同樣是個具有視頻展示的python庫,主要用于小界面任何圖像,視頻和體數據的讀寫。比如說你可以使用  imwrite()來讀寫圖片,minwrite()用于任何視頻和GIF, volwrite()用于體數據, write()用于流數據。

原創文章,作者:以馬內利,如若轉載,請注明出處:http://www.www58058.com/1333

(4)
以馬內利以馬內利
上一篇 2015-03-26 11:52
下一篇 2015-03-26 14:59

相關推薦

  • week5

    1、顯示當前系統上root、fedora或user1用戶的默認shell; useradd fedora useradd user1 egrep "^(root|fedora|user1)" /etc/passwd | cut -d: -f1,7 2、找出/e…

    Linux干貨 2016-11-24
  • CentOS6.7上編譯安裝php

    環境:CentOS6.7,minimal安裝。 前提條件:安裝了編譯環境,安裝了Apache/Nginx,安裝了MySQL/MariaDB。具體安裝見:http://www.www58058.com/16583    http://www.www58058.com/17497  1、解決依賴關系: 請配置好yum源(系統安裝源及…

    Linux干貨 2016-06-03
  • Python基礎之if while for循環練習

    if for while循環練習 沒有邊界的最好用while,有邊界的最好用for 1.給定一個不超過5位數的正整數,判斷其有幾位 num = int(input()) if num<10: print(‘一位’) elif num<100: print(‘兩位’) elif num<1000: print(‘三位’) elif num&l…

    2017-09-16
  • Linux基礎知識之軟硬鏈接

    系統環境:    該博文以CentOS6.8_x86_64系統為基礎,Xshell5遠程登錄CentOS6.8系統,以root身份登錄系統。 為什么要學習符號(軟)鏈接和硬鏈接?    符號(軟)鏈接和硬鏈接是Linux文件系統中的一個重要的概念,軟硬鏈接的學習過程中會涉及一些文件系統中的索引節點(inode),索引節…

    Linux干貨 2016-08-02
  • bash的基本特性之globbing,IO重定向及管道

    bash的基本特性之globbing,IO重定向及管道 giobbing:文件名通配 在bash的操作環境中有一個分廠有用的功能那就是gilobbing:文件名通配,這樣我們在處理數據的時候就更方便了。下面我們來羅列一些常用的通配符。(注:globbing是做整體的文件名匹配而非部分) 匹配模式:元字符 *:表示匹配任意長度的任意字符  &nbsp…

    Linux干貨 2016-12-19
  • 集群基本介紹

    Linux Cluster: Cluster:計算機集合,為解決某個特定問題組合起來形成的單個系統; Linux Cluster類型: LB:Load Balancing,負載均衡;主機來平均訪問的壓力。由負載均衡器和多個后端主機分擔主。 HA:High Availiablity,高可用,靠冗余節點實現;提高服務的可用性,有多個負載均衡器(和備份)來接收來自…

    Linux干貨 2016-10-30
欧美性久久久久