目录
  • 前言
  • gif包概述
  • 动画原理
  • gif包解读
    • options类
    • frames函数
    • save函数
  • gif包生成gif动画实践
    • 以心形曲线为例比较gif包和animation模块实现动画的差异
      • 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")
      

      以心形曲线为例比较gif包和animation模块实现动画的差异

      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)
      

      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, "love1.gif"):
          for i in range(1, len(x)):
              of = plot_love(x[i], y[i])
              writer.grab_frame()
      

      比较结果

      通过比较可知gif包的实现方式和matplotlib中利用PillowWriter实现方式类似,更偏底层一些,这样遇到比较复杂的绘图时更灵活。

      总结

      声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。