美文网首页
python制作演示动画

python制作演示动画

作者: ElephantKing | 来源:发表于2019-11-13 11:46 被阅读0次

先看效果图

sin_cos.gif

循序渐进

  • 一个超级简单的绘图示例:


    sample1.png
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

#建立一个画板
fig = plt.figure()
#把画板分成1x1的栅格,并使用1号格子
#ax = fig.add_subplot(2,2,4)分成2x2的栅格,使用4号格子
ax = fig.add_subplot(1,1,1)
x = np.linspace(0, 5, 20)
#在ax的对象上建立'两条线',根据需要可以命名
line1 = ax.plot(x, x**2)
line2 = ax.plot(x, x**3)
#根据对象的所有点的坐标,依次连接成线
plt.show()
  • 让图动起来


    exp.gif
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import cv2
import imageio
from PIL import Image, ImageSequence

#建立一个画板
fig = plt.figure()
#把画板分成1x1的栅格,并使用1号格子
#ax = fig.add_subplot(2,2,4)分成2x2的栅格,使用4号格子
ax = fig.add_subplot(1,1,1)
x = np.linspace(0, 5, 20)
#在ax的对象上建立'两条线',根据需要可以命名,因为需要让line1动起来,所以给他命名了
xdata,ydata = [],[]
line1, = ax.plot([], [])

ax.plot(x, x**3)
#根据对象的所有点的坐标,依次连接成线

def init():
    global xdata,ydata
    del xdata[:]
    del ydata[:]

def data_gen():
    global x
    for i in x:
        yield i,i**2

def update(xy_pos):
    global xdata,ydata
    xpos,ypos = xy_pos
    xdata.append(xpos)
    ydata.append(ypos)
    line1.set_data(xdata,ydata)
    
anima = animation.FuncAnimation(fig, update, data_gen, blit=False, interval=3,repeat=True, init_func=init)
plt.show()

稍微解释一下动图的机制:
从最开始的栗子可以知道,这种绘制曲线的方法的本质是用线段连接散列的点,由于相邻两个点的间距很小,绘制的曲线看起来比较平滑。同时,程序始终依据曲线的散列的点来绘制,而这些点存放在两个数组里,横纵左边分别在一个数组,用xdata,ydata=line1.get_data(),可以得到曲线line1的已经存在的点,数组里所有的点都会被绘制。关键的来了,如果相邻两次绘制过程中,如果在数组里添加了新的点,那么后一次绘制就会多绘制一些点,表现出来的样子就是曲线伸长,这就是动图的原理。
是时候着重介绍一下函数animation.FuncAnimation

fig:画板对象
update:相邻两次绘制之间调用,用以更新曲线绘制的节点,实现动图

data_gen:是一个序列,用于生产新的节点,其返回值会被当做参数传给update函数,供update函数处理。比如data_gen是一个列表,那么列表中每一个值会依次传递到update函数中,上面代码中date_gen是一个生成器,每次返回一个值(可能是int,也可以是任意其他类型),传递到update函数中。

interval:相邻两次绘制的时间间隔
repeat:绘制完曲线后,是否反复绘制,即从新开始绘制
init_func:每次重新绘制曲线前都要执行的初始化函数

最开始的栗子的代码

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import cv2
import imageio
from PIL import Image, ImageSequence

def data_gen(t=0.1):
    cnt = 0
    x = np.linspace(0,np.pi*2,100)
    while cnt < np.pi*2:
        cnt += 0.1
        yield x
        for i in range(len(x)):
            x[i] = x[i] + t

def init():
    ax.set_ylim(-10, 10)
    ax.set_xlim(0, 20)
    del xdata[:]
    del ydata[:]
    sin_line.set_data(xdata, ydata)

fig = plt.figure()
ax = fig.add_subplot(1,1,1)

#竖直的直线,lw是线的宽度
line2, = ax.plot([2*np.pi,2*np.pi], [-1,1], lw=1)
#[-1,1]分为100份
dpi_range = np.linspace(-1,1,100)
#上半圆和下半圆
ax.plot(dpi_range+10, np.sqrt(1-dpi_range*dpi_range), 'b',lw=1)
ax.plot(dpi_range+10, -np.sqrt(1-dpi_range*dpi_range),'b', lw=1)

#水平线line3
circle_xdata = [2*np.pi, 9]
circle_ydata = [0, 0]
line3, = ax.plot(circle_xdata,circle_ydata,'r--',lw=1)
#圆心到圆上的线line4
d_xdata = [10, 9]
d_ydata = [0, 0]
line4, = ax.plot(d_xdata,d_ydata,'g-',lw=1)
#将面板栅格化,可以注释看效果很好懂。
ax.grid()
#正弦曲线
sin_line, = ax.plot([], [], lw=1)
#正弦曲线上的点的横纵坐标集合,初始为空
xdata = list(np.linspace(0,np.pi*2,100))
ydata = []
#让横纵坐标轴单位长度在屏幕上一样长
plt.axis('equal')
theta1 = 0.0
def run(data):
    #data是data_gen的返回值,是一个数组
    #xdata = list(data)
    ydata = np.sin(list(data))
    xdata  = np.linspace(0,np.pi*2,100)

    sin_line.set_data(xdata, ydata)
    global theta1
    #水平线两个端点的坐标更新
    circle_xdata[1] = 10 - np.cos(theta1)
    circle_ydata[0] = np.sin(theta1)
    circle_ydata[1] = np.sin(theta1)
    line3.set_data(circle_xdata, circle_ydata)
    #圆心到圆上的线断端点坐标更新
    d_xdata[1] = 10 - np.cos(theta1)
    d_ydata[1] = np.sin(theta1)
    line4.set_data(d_xdata,d_ydata)
    theta1 = theta1 + 0.1

ani = animation.FuncAnimation(fig, run, data_gen, blit=False,
interval=40,repeat=True, init_func=init)
plt.show()

一颗小心心

一颗小心心
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import cv2
import math
import imageio
from PIL import Image, ImageSequence

#建立一个画板
fig = plt.figure()
#把画板分成1x1的栅格,并使用1号格子
#ax = fig.add_subplot(2,2,4)分成2x2的栅格,使用4号格子
ax = fig.add_subplot(1,1,1)
#有趣的是把下面的100换成500竟然不能生成完整的GIF
#难道有帧数限制
x = np.linspace(-1.139, 1.139, 100)
#在ax的对象上建立'两条线',根据需要可以命名
xdata,ydata = [],[]
line1, = ax.plot([], [],'r')
line2, = ax.plot([],[],'r')
ydata2 = []
#根据对象的所有点的坐标,依次连接成线

def init():
    global xdata,ydata,ydata2
    ax.set_ylim(-2, 2)
    ax.set_xlim(-2, 2)
    del xdata[:]
    del ydata[:]
    del ydata2[:]
    return line1,line2

def data_gen():
    global x
    for i in x:
        b = pow(i, 2)
        b = -pow(b, 1/3)
        c = pow(i, 2) - 1
        delta = pow(b,2) - 4 * c
        if pow(b,2) - 4 * c < 0:
            delta = 0
        #print(i,b,c)
        yield i,(-b+pow(delta, 1/2))/2,(-b-pow(delta, 1/2))/2

def update(xy_pos):
    global xdata,ydata,ydata2
    xpos,ypos,ypos2 = xy_pos
    xdata.append(xpos)
    ydata.append(ypos)
    ydata2.append(ypos2)
    line1.set_data(xdata,ydata)
    line2.set_data(xdata,ydata2)
    return line1,line2
    
anima = animation.FuncAnimation(fig, update, data_gen, blit=False, 
interval=50,repeat=True, init_func=init)
plt.show()

#下面的代码是用来生成GIF的
anima.save('heart.gif', writer='pillow')

im = Image.open('heart.gif')
images = []
iter = ImageSequence.Iterator(im)
index = 1
for frame in iter:
    frame.save('heart%d.png' % index)
    index = index + 1

for i in range(1, index):
    images.append(imageio.imread("heart" + str(i)+".png"))

print(len(images))
imageio.mimsave("heart2.gif",images,fps=30)

相关文章

网友评论

      本文标题:python制作演示动画

      本文链接:https://www.haomeiwen.com/subject/ipdqictx.html