如何提升Matplotlib绘制的水波干涉动画帧率?
水波干涉可视化动画帧率优化
使用Matplotlib和NumPy实现水波干涉可视化,已完成所有振幅计算,但动画帧率过低,尝试多种优化无效,确认计算过程耗时占比不高,目标将帧率提升至约12fps。相关代码如下:
import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.axes_grid1 import make_axes_locatable import matplotlib.animation as animation """ 1.parameter setup """ L, sep, N, k, cut = 6, 2, 500, 5*2, 0.8 # 0.5倍寬度, 波源與原點距離, 每邊分割數量, 角波數, z軸範圍 fps, frn = 24, 50 # 每秒影格數量, 影格總數 x = np.linspace(-2*L, 2*L, N) # x axis y = np.linspace(-L, L, N) # y axis X, Y = np.meshgrid(x, y) # 2D matrix j = complex(0, 1) # 根號 -1 """ 2.設定計算振幅的函數, 計算每個位置的振幅並存入陣列 """ # 自訂函式, 計算每個位置對應的振幅 """ def func(x, y, t): r1 = np.sqrt(x**2 + y**2) # 點波源1 for i in range(250): r1[i] = y[i]+L r2 = np.sqrt((x+sep)**2 + y**2) # 點波源2 z = np.exp(j*k*r1)/k*3 for i in range(250, 499): z[i] = np.exp(j*k*(r1[249]+r1[i]))/k*3 z_real = np.real(z*np.exp(-j*t)) return z_real # 回傳實部 """ def func(x, y, t, n, a, b): """ r0 = np.sqrt((x-6)**2 + y**2) r1 = np.sqrt((x-5)**2 + y**2) ... r11 = np.sqrt((x+5)**2 + y**2) r12 = np.sqrt((x+6)**2 + y**2) """ for i in range(n): r = np.sqrt((x-a[i])**2 + (y-b[i])**2) if(i == 0): z = np.exp(j*k*r)/r else: z += np.exp(j*k*r)/r return np.real(z*np.exp(-j*t)) # 回傳實部 Z = np.zeros((N, N, frn)) # 儲存振幅用的2維陣列 T = np.linspace(0, 2*np.pi, frn) # 儲存時間用的1維陣列 # user input n=int(input("輸入波源數量:")) print('點波源位置輸入格式: x , y') a=[] b=[] for i in range(n): temp=input(f"位置{i}:").split(',') a.append(int(temp[0])) b.append(int(temp[1])) # 計算每個時刻每個位置對應的振幅, 有加cut效果較佳 for i in range(frn): Z[:, :, i] = func(X, Y, T[i], n,a,b).clip(-cut, cut) """ 3.繪圖 """ fig = plt.figure(figsize=(7, 6), dpi=100) # 開啟繪圖視窗 ax = fig.gca() ax.set_aspect(1.0) # 使圖片長寬變成1:1 # 以某個預設的colormap為基底, 修改成對應到 -cut ~ +cut 的colormap mappable = plt.cm.ScalarMappable(cmap=plt.cm.jet) mappable.set_array(np.arange(-cut, cut, 0.1)) # 在圖片右側加上color bar, 高度與圖片相同 divider = make_axes_locatable(ax) cax = divider.append_axes("right", size="5%", pad=0.05) plt.colorbar(mappable, cax=cax) # 自訂函式, 先移除前一張圖, 再畫出下一張圖 def update(frame_number): plot[0] = ax.contourf(X, Y, Z[:, :, frame_number], cmap=mappable.cmap, norm=mappable.norm) # t = 0 的圖片 plot = [ax.contourf(X, Y, Z[:, :, 0], cmap=mappable.cmap, norm=mappable.norm)] # 產生動畫, 目標為繪圖物件fig, 使用自訂函式update更新圖片, 圖片總數為frn, 時間間隔為interal, 單位為ms ani = animation.FuncAnimation(fig, update, frn, interval=1000/fps) plt.show() # 顯示圖片 # ani.save('TwoSourcesInterference2D.gif', writer='imagemagick', fps=fps) # 儲存圖片
优化方案
帧率低的核心原因是**contourf属于矢量绘图函数,每次重绘都会重新计算等高线并渲染大量矢量图形,开销极大**。针对这个问题,给出以下优化措施:
1. 替换contourf为imshow
imshow是位图渲染函数,适合展示预计算好的二维数组,渲染速度远快于contourf。
2. 优化动画更新逻辑
不再每次创建新的绘图对象,而是直接更新已有对象的数据源,避免重复创建和销毁图形元素的开销。
3. 调整帧率参数
将目标帧率设为12fps,对应interval参数为1000/12(约83ms)。
修改后的完整绘图代码段
""" 3.繪圖 (優化版) """ fig = plt.figure(figsize=(7, 6), dpi=100) ax = fig.gca() ax.set_aspect(1.0) # 設定顏色映射範圍 norm = plt.Normalize(-cut, cut) mappable = plt.cm.ScalarMappable(cmap=plt.cm.jet, norm=norm) mappable.set_array([]) # 添加顏色條 divider = make_axes_locatable(ax) cax = divider.append_axes("right", size="5%", pad=0.05) plt.colorbar(mappable, cax=cax) # 使用imshow初始化圖像,設置extent匹配坐標軸 im = ax.imshow(Z[:, :, 0], cmap=plt.cm.jet, norm=norm, extent=[x.min(), x.max(), y.min(), y.max()], origin='lower') # 更新函數:僅更換數據源 def update(frame_number): im.set_data(Z[:, :, frame_number]) return [im] # 生成動畫,設置目標帧率12fps fps_target = 12 ani = animation.FuncAnimation(fig, update, frames=frn, interval=1000//fps_target, blit=True) plt.show()
额外优化建议
- 若仍觉得帧率不够,可适当降低
N(当前为500),减少像素点数量,进一步降低渲染负载。 - 将用户输入的波源位置转换为NumPy数组,避免函数内循环的列表索引开销:
# 替换用户输入部分的a、b为numpy数组 a = np.array(a, dtype=np.float64) b = np.array(b, dtype=np.float64)
内容的提问来源于stack exchange,提问作者林育正
相关产品推荐
相关产品推荐

