使用
matplotlib
生成gif動畫的方法有很多,一般正常使用
matplotlib
的
animation
子產品的
FuncAnimation
函數實作。在
matplotlib
官網看到了第三方動畫包
gif
的介紹。
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
包非常簡潔,隻有一個單獨的檔案
gif.py
,檔案主要包含
options
類、
frames
和
save
兩個函數。
options
類
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
函數
frames
裝飾器函數,通過對應包編寫自定義繪圖函數生成單幀圖像。
save
函數
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動畫實踐
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")
以心形曲線為例比較 gif
包和 animation
子產品實作動畫的差異
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()
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
函數實作方式
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
類實作方式
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
實作方式類似,更偏底層一些,這樣遇到比較複雜的繪圖時更靈活。
gif
matplotlib
PillowWriter