Как я могу ускорить генерацию MP4 с помощью Matplotlib's Animation Writer? - PullRequest
0 голосов
/ 13 февраля 2019

Я использую matplotlib для создания графической анимации некоторых данных.Время сбора данных составляет около 4 часов, поэтому я ожидаю, что анимация составит около 4 часов.Однако генерация 60-секундного видео занимает приблизительно 15 минут.Таким образом, общее расчетное время выполнения для создания 4-часового видео составляет 2,5 дня.Я предполагаю, что делаю что-то невероятно неэффективное.Как я могу ускорить создание анимации с помощью matplotlib?

create_graph.py

import matplotlib.pyplot as plt
import matplotlib.animation as animation
import matplotlib
import pandas as pd
import numpy as np

matplotlib.use("Agg")

frame = pd.read_csv("tmp/total.csv")
min_time = frame.iloc[0]["time"]
max_time = frame.iloc[-1]["time"]
total_time = max_time - min_time

hertz_rate = 50
window_length = 5
save_count = hertz_rate * 100

def data_gen():
    current_index_of_matching_ts = 0
    t = data_gen.t
    cnt = 0
    while cnt < save_count:
        print("Done: {}%".format(cnt/save_count*100.0))
        predicted = cnt * (1.0/hertz_rate)
        while frame.iloc[current_index_of_matching_ts]["time"] - min_time <= predicted and current_index_of_matching_ts < len(frame) - 1:
            current_index_of_matching_ts = current_index_of_matching_ts + 1

        y1 = frame.iloc[current_index_of_matching_ts]["var1"]
        y2 = frame.iloc[current_index_of_matching_ts]["var2"]
        y3 = frame.iloc[current_index_of_matching_ts]["var3"]
        y4 = frame.iloc[current_index_of_matching_ts]["var4"]
        y5 = frame.iloc[current_index_of_matching_ts]["var5"]
        y6 = frame.iloc[current_index_of_matching_ts]["var6"]
        y7 = frame.iloc[current_index_of_matching_ts]["var7"]
        y8 = frame.iloc[current_index_of_matching_ts]["var8"]
        y9 = frame.iloc[current_index_of_matching_ts]["var9"]
        t = frame.iloc[current_index_of_matching_ts]["time"] - min_time
        # adapted the data generator to yield both sin and cos
        yield t, y1, y2, y3, y4, y5, y6, y7, y8, y9
        cnt+=1

data_gen.t = 0

# create a figure with two subplots
fig, (ax1, ax2, ax3, ax4, ax5, ax6, ax7, ax8, ax9) = plt.subplots(9,1,figsize=(7,14)) # produces a video of 700 × 1400

# intialize two line objects (one in each axes)
line1, = ax1.plot([], [], lw=2, color='b')
line2, = ax2.plot([], [], lw=2, color='b')
line3, = ax3.plot([], [], lw=2, color='b')
line4, = ax4.plot([], [], lw=2, color='g')
line5, = ax5.plot([], [], lw=2, color='g')
line6, = ax6.plot([], [], lw=2, color='g')
line7, = ax7.plot([], [], lw=2, color='r')
line8, = ax8.plot([], [], lw=2, color='r')
line9, = ax9.plot([], [], lw=2, color='r')
line = [line1, line2, line3, line4, line5, line6, line7, line8, line9]

# the same axes initalizations as before (just now we do it for both of them)
for ax in [ax1, ax2, ax3, ax4, ax5, ax6, ax7, ax8,  ax9]:
    ax.set_ylim(-1.1, 1.1)
    ax.grid()

# initialize the data arrays 
xdata, y1data, y2data, y3data, y4data, y5data, y6data, y7data, y8data, y9data = [], [], [], [], [], [], [], [], [], []

my_gen = data_gen()
for index in range(hertz_rate*window_length-1):
    t, y1, y2, y3, y4, y5, y6, y7, y8, y9 = my_gen.__next__()
    xdata.append(t)
    y1data.append(y1)
    y2data.append(y2)
    y3data.append(y3)
    y4data.append(y4)
    y5data.append(y5)
    y6data.append(y6)
    y7data.append(y7)
    y8data.append(y8)
    y9data.append(y9)


def run(data):
    # update the data
    t, y1, y2, y3, y4, y5, y6, y7, y8, y9 = data
    xdata.append(t)
    y1data.append(y1)
    y2data.append(y2)
    y3data.append(y3)
    y4data.append(y4)
    y5data.append(y5)
    y6data.append(y6)
    y7data.append(y7)
    y8data.append(y8)
    y9data.append(y9)

    # axis limits checking. Same as before, just for both axes
    for ax in [ax1, ax2, ax3, ax4, ax5, ax6, ax7, ax8, ax9]:
        ax.set_xlim(xdata[-1]-5.0, xdata[-1])

    # update the data of both line objects
    line[0].set_data(xdata, y1data)
    line[1].set_data(xdata, y2data)
    line[2].set_data(xdata, y3data)
    line[3].set_data(xdata, y4data)
    line[4].set_data(xdata, y5data)
    line[5].set_data(xdata, y6data)
    line[6].set_data(xdata, y7data)
    line[7].set_data(xdata, y8data)
    line[8].set_data(xdata, y9data)

    return line

ani = animation.FuncAnimation(fig, run, my_gen, blit=True, interval=20, repeat=False, save_count=save_count)

Writer = animation.writers['ffmpeg']
writer = Writer(fps=hertz_rate, metadata=dict(artist='Me'), bitrate=1800)
ani.save('lines.mp4', writer=writer)

1 Ответ

0 голосов
/ 18 февраля 2019

Так что я отвечаю на свой вопрос здесь, так что если вы найдете это удовольствие!

Вот некоторые факты

  • matplotlib создает высококачественные графики
  • генерирует matplotlibГрафики медленно по сравнению с некоторыми другими библиотеками, такими как PyQWT (для скорости используются привязки c ++)
  • Создание графика в реальном времени из 4 часов данных на моем Mac займет около 20 часов.

Чтобы решить мою проблему, я создал отдельные файлы и позже соединил их вместе.Я использовал многопроцессорную библиотеку .

generate_graphs.py

import multiprocessing as mp
from multiprocessing import Pool
from make_video_graph_mp4 import write_chart_to_file_wrapper


total_parts = 6

if __name__ == '__main__':
    #spawn is critical to not share plt across threads.
    mp.set_start_method('spawn')
    with Pool() as p:
        print(p.map(write_chart_to_file_wrapper, [[i, total_parts] for i in range(total_parts)]))

make_video_graph_mp4.py

def write_chart_to_file(my_part, parts):
    # ... code to create part my_part/parts of the video.
    Writer = animation.writers['ffmpeg']
    writer = Writer(fps=hertz_rate, metadata=dict(artist='Me'), bitrate=1800)
    filename = 'out/videos/{}-lines{}-{}.mp4'.format(band_name, start_index, end_index)        
    ani.save(filename, writer=writer, dpi=100)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...