把Python爬的數據滾動起來!你會發現一件神奇的事情!動畫可視化

来源:https://www.cnblogs.com/sm123456/archive/2018/05/11/9026569.html
-Advertisement-
Play Games

Python 中有很多不錯的數據可視化庫,但是極少能渲染 GIF 圖或視頻動畫效果。本文就分享一下如何用 MoviePy 作為其他可視化庫的通用插件,製作動畫可視化效果,畢竟這年頭,沒圖不行,有動圖更好。 MoviePy 能讓我們用函數 make_frame(t) 自定義動畫,函數會返回和時間 t ...


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 著色器後製作而成:

製作該動畫的代碼地址: https:// gist.github.com/Zulko/5 4e5468759396c5cbbd2

 

製作該動畫的代碼地址: https:// gist.github.com/Zulko/4 dcaf3e38fdc118f22a3

 

基於 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)

零基礎入門視頻。項目實戰視頻!大牛答疑群:125240963

 

簡單來說,通過背景顏色我們就可以得知分類器辨識黑色點和白色點屬於哪裡。剛開始並不明顯,但隨著越來越多的數據點出現,這些點的分佈逐漸呈月牙形區域。

 

基於 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 萬歲!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在研究學術之餘,來複習一下java的SpringMVC框架,最近也沒什麼項目,所以也有一段時間沒有看這個框架了,都有點陌生了,現在每天都在看論文,研究方案,做實驗,在論文看不下去的時候就來學習一下SPringMVC也是不錯的選擇,哈哈哈!!!!!! 1. springmvc框架 1.1 什麼是spr ...
  • 一.protobuf 安裝 protobuf版本:2.6.1 下載地址:https://github.com/google/protobuf/archive/v2.6.1.zip 解壓之後進入目錄 修改autogen.sh 將autogen.sh內的上述內容修改為 然後執行autogen.sh ./ ...
  • DTL 變數 標簽 過濾器 1. 語法:{ { 變數|過濾器 }},例如{ { name|lower }},表示將變數name的值變為小寫輸出 2. 使用管道符號 (|)來應用過濾器 3. 通過使用過濾器來改變變數的計算結果 4. 可以在if標簽中使用過濾器結合運算符 ...
  • pandas 基礎 serise 0 4 1 7 2 5 3 3 dtype: int64 array([ 4, 7, 5, 3], dtype=int64) RangeIndex(start=0, stop=4, step=1) 1 7 3 3 dtype: int64 1 7 2 5 dtype ...
  • 這是java的一條規則。那麼為什麼會有這條規則呢?要想弄懂這個問題,就需要弄懂局部內部類對象和局部變數的生命周期的誰更長的問題。 首先,看一段代碼,以沒有將變數聲明為final的代碼作為例子,代碼如下: 如上面的第7行代碼所示,變數x沒有被聲明為final,如果是這樣的話,當執行完第26行的outM ...
  • 本次和大家分享的是dubbo框架應用的初略配置和zookeeper註冊中心的使用;說到註冊中心現在我使用過的只有兩種:zookeeper和Eureka,zk我結合dubbo來使用,而Eureka結合springcloud使用,因此後面將和大家分享一些關於微服務的一些篇章,希望對你有好的幫助。 安裝註 ...
  • T1 簽到題,兩種情況分別計算然後取個min T2 不會QWQ.... 首先一個很顯然的性質就是當$w >8$或$w < -9$的時候是無解的 否則,我們令$D_1=x$,$D_N = x +W$,這樣其它的數就可以任意取了,有$10^{N - 2}$種方案 然後把$x$的取值乘上 具體見代碼吧 T ...
  • Esc:命令行模式 i:插入命令 a:附加命令 o:打開命令 c:修改命令 r:取代命令 s:替換命令 以上進入文本輸入模式 : 進入末行模式 末行模式: w:保存 q:退出,沒保存則無法退出 wq:保存並且退出 x:保存退出 q!:強制退出 q!:強制退出 輸入模式時: i: 插入游標前一個字元 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...