先看效果图

循序渐进
-
一个超级简单的绘图示例:
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)
网友评论