天天看點

Python通過matplotlib包和gif包生成gif動畫

使用​

​matplotlib​

​​生成gif動畫的方法有很多,一般正常使用​

​matplotlib​

​​的​

​animation​

​​子產品的​

​FuncAnimation​

​​函數實作。在​

​matplotlib​

​​官網看到了第三方動畫包​

​gif​

​的介紹。

​gif​

​包概述

​gif​

​​包是支援 ​

​Altair​

​​, ​

​matplotlib​

​​和​

​Plotly​

​​的動畫擴充。

​​

​gif​

​​依賴​

​PIL​

​​,即​

​pillow​

​​,要求​

​Pillow>=7.1.2​

​​。

安裝​​

​gif​

​​包,​

​pip install gif​

動畫原理

所有​

​動畫​

​​都是由​

​幀(frame)​

​​構成的,一幀就是一幅靜止的畫面,連續的幀就形成動畫。我們通常說幀數,簡單地說,就是在1秒鐘時間裡傳輸的圖檔的幀數,也可以了解為圖形處理器每秒鐘能夠重新整理幾次,通常用​

​fps(Frames Per Second)​

​表示。制作動畫的關鍵:如何生成幀,每秒多少幀。

​gif​

​包解讀

​gif​

​​包非常簡潔,隻有一個單獨的檔案​

​gif.py​

​​,檔案主要包含​

​options​

​​類、​

​frames​

​​和​

​save​

​兩個函數。

​options​

​類

提供精簡版 的​

​Altair​

​​, ​

​matplotlib​

​​和​

​Plotly​

​​的儲存或輸出設定。以​

​matplotlib​

​為例,提供以下設定。

  • dpi (int): The resolution in dots per inch
  • facecolor (colorspec): The facecolor of the figure
  • edgecolor (colorspec): The edgecolor of the figure
  • transparent (bool): If True, the axes patches will all be transparent

設定方法:​

​gif.options.matplotlib["dpi"] = 300​

​​原理:​

​options​

​​在構造函數中建立​

​matplotlib​

​​字典儲存配置,随後傳遞給底層的​

​matplotlib​

​包。

​frames​

​函數

裝飾器函數,通過對應包編寫自定義繪圖函數生成單幀圖像。

​save​

​函數

根據幀序列生成動畫。

def save(frames, path, duration=100, unit="milliseconds", between="frames", loop=True):
    """Save decorated frames to an animated gif.
    - frames (list): collection of frames built with the gif.frame decorator
    - path (str): filename with relative/absolute path
    - duration (int/float): time (with reference to unit and between)
    - unit {"ms" or "milliseconds", "s" or "seconds"}: time unit value
    - between {"frames", "startend"}: duration between "frames" or the entire gif ("startend")
    - loop (bool): infinitely loop the animation      

​frames​

​即根據​

​@gif.frame​

​裝飾的繪圖函數生成的幀的序列,此處根據需要自定義。

​​

​duration​

​即持續時間,由機關​

​unit​

​和模式​

​between​

​決定,預設為​

​frames​

​為幀間的時間間隔。

​​

​unit​

​即持續時間機關,支援毫秒和秒,預設為毫秒。

​​

​between​

​即持續時間計算模式,預設​

​frames​

​即​

​duration​

​為幀之間的時間間隔,​

​startend​

​模式時​

​duration=duration /len(frames)​

​,即​

​duration​

​為所有幀—整個動畫的持續時間。

​gif​

​包生成gif動畫實踐

import random
from matplotlib import pyplot as plt
import gif

# 構造資料
x = [random.randint(0, 100) for _ in range(100)]
y = [random.randint(0, 100) for _ in range(100)]
# 設定選項
gif.options.matplotlib["dpi"] = 300


# 使用gif.frame裝飾器構造繪圖函數,即如何生成靜态的幀
@gif.frame
def plot(i):
    xi = x[i * 10:(i + 1) * 10]
    yi = y[i * 10:(i + 1) * 10]
    plt.scatter(xi, yi)
    plt.xlim((0, 100))
    plt.ylim((0, 100))


# 構造幀序列frames,即把生成動畫的所有幀按順序放在清單中
frames = []
for i in range(10):
    frame = plot(i)
    frames.append(frame)
# 根據幀序列frames,動畫持續時間duration,生成gif動畫
gif.save(frames, 'example.gif', duration=3.5, unit="s", between="startend")      
Python通過matplotlib包和gif包生成gif動畫

以心形曲線為例比較​

​gif​

​​包和​

​animation​

​子產品實作動畫的差異

from matplotlib import pyplot as plt
import numpy as np

t = np.linspace(0, 6, 100)
x = 16 * np.sin(t) ** 3
y = 13 * np.cos(t) - 5 * np.cos(2 * t) - 2 * np.cos(3 * t) - np.cos(4 * t)
fig = plt.figure(figsize=(5, 3), dpi=100)
plt.scatter(x, y)
plt.show()      

心形曲線繪制

from matplotlib import pyplot as pltimport numpy as np
t = np.linspace(0, 6, 100)x = 16 * np.sin(t) ** 3y = 13 * np.cos(t) - 5 * np.cos(2 * t) - 2 * np.cos(3 * t) - np.cos(4 * t)fig = plt.figure(figsize=(5, 3), dpi=100)plt.scatter(x, y)plt.show()      
Python通過matplotlib包和gif包生成gif動畫

​gif​

​包的實作方式

import numpy as np
import gif
from matplotlib import pyplot as plt

t = np.linspace(0, 6, 100)
x = 16 * np.sin(t) ** 3
y = 13 * np.cos(t) - 5 * np.cos(2 * t) - 2 * np.cos(3 * t) - np.cos(4 * t)

@gif.frame
def plot_love(x, y):
    plt.figure(figsize=(5, 3), dpi=100)
    plt.scatter(x, y, 60, c="r", alpha=0.7, marker=r"$\heartsuit$")
    plt.axis("off")
    
frames = []
for i in range(1, len(x)):
    of = plot_love(x[:i], y[:i])
    frames.append(of)

gif.save(frames, "love.gif", duration=80)      
import numpy as npimport giffrom matplotlib import pyplot as plt
t = np.linspace(0, 6, 100)x = 16 * np.sin(t) ** 3y = 13 * np.cos(t) - 5 * np.cos(2 * t) - 2 * np.cos(3 * t) - np.cos(4 * t)
@gif.framedef plot_love(x, y):    plt.figure(figsize=(5, 3), dpi=100)    plt.scatter(x, y, 60, c="r", alpha=0.7, marker=r"$\heartsuit$")    plt.axis("off")frames = []for i in range(1, len(x)):    of = plot_love(x[:i], y[:i])    frames.append(of)
gif.save(frames, "love.gif", duratinotallow=80)      

​matplotlib​

​​ 正常​

​FuncAnimation​

​函數實作方式

from matplotlib import pyplot as plt
import matplotlib.animation as animation
import numpy as np

t = np.linspace(0, 6, 100)
x = 16 * np.sin(t) ** 3
y = 13 * np.cos(t) - 5 * np.cos(2 * t) - 2 * np.cos(3 * t) - np.cos(4 * t)
data=[i for i in zip(x,y)]

def plot_love(data):
    x, y = data
    plt.scatter(x, y, 60, c="r", alpha=0.7, marker=r"$\heartsuit$")

fig=plt.figure(figsize=(5, 3), dpi=100)
plt.axis("off")
animator = animation.FuncAnimation(fig, plot_love, frames=data, interval=80)
animator.save("love.gif", writer='pillow')      

​matplotlib​

​​底層​

​PillowWriter​

​類實作方式

from matplotlib import pyplot as plt
import matplotlib.animation as animation
import numpy as np

t = np.linspace(0, 6, 100)
x = 16 * np.sin(t) ** 3
y = 13 * np.cos(t) - 5 * np.cos(2 * t) - 2 * np.cos(3 * t) - np.cos(4 * t)


def plot_love(x, y):
    plt.scatter(x, y, 60, c="r", alpha=0.7, marker=r"$\heartsuit$")


fig = plt.figure(figsize=(5, 3), dpi=100)
plt.axis("off")

writer = animation.PillowWriter(fps=15)
with writer.saving(fig, "love21.gif", dpi=100):
    for i in range(1, len(x)):
        plot_love(x[i], y[i])
        writer.grab_frame()      
from matplotlib import pyplot as pltimport matplotlib.animation as animationimport numpy as np
t = np.linspace(0, 6, 100)x = 16 * np.sin(t) ** 3y = 13 * np.cos(t) - 5 * np.cos(2 * t) - 2 * np.cos(3 * t) - np.cos(4 * t)

def plot_love(x, y):    plt.scatter(x, y, 60, c="r", alpha=0.7, marker=r"$\heartsuit$")

fig = plt.figure(figsize=(5, 3), dpi=100)plt.axis("off")
writer = animation.PillowWriter(fps=15)with writer.saving(fig, "love21.gif", dpi=100):    for i in range(1, len(x)):        plot_love(x[i], y[i])        writer.grab_frame()      

通過比較可知​

​gif​

​包的實作方式和​

​matplotlib​

​中利用​

​PillowWriter​

​實作方式類似,更偏底層一些,這樣遇到比較複雜的繪圖時更靈活。