2016-12-08 38 views
2

我希望找到一種方法來優化以下情況。我有一個用matplotlib imshow創建的大輪廓圖。然後我想用這個輪廓圖來創建大量的png圖像,其中每個圖像是輪廓圖像的一小部分,通過改變x和y的限制以及寬高比。matplotlib savefig性能,在循環內節省多個PNG

因此,循環中沒有繪圖數據發生變化,只有軸限制和縱橫比在每個png圖像之間變化。

以下MWE在「無花果」文件夾中創建70個PNG圖像,展示簡化的想法。大約80%的運行時間被fig.savefig('figs/'+filename)佔用。

我看着下面沒有想出一個改進:

  • matplotlib的替代,重點是速度 - 我一直在努力尋找任何例子/輪廓/表面圖的文檔有類似的要求
  • 多處理 - 類似的問題我在這裏看到似乎要求fig = plt.figure()ax.imshow在循環中被調用,因爲無花果和斧頭不能被醃製。在我的情況下,這將比通過實現多處理獲得的任何速度收益更昂貴。

我很感謝您的任何見解或建議。

import numpy as np 
import matplotlib as mpl 
mpl.use('agg') 
import matplotlib.pyplot as plt 
import time, os 

def make_plot(x, y, fix, ax): 
    aspect = np.random.random(1)+y/2.0-x 
    xrand = np.random.random(2)*x 
    xlim = [min(xrand), max(xrand)] 
    yrand = np.random.random(2)*y 
    ylim = [min(yrand), max(yrand)] 
    filename = '{:d}_{:d}.png'.format(x,y) 

    ax.set_aspect(abs(aspect[0])) 
    ax.set_xlim(xlim) 
    ax.set_ylim(ylim) 
    fig.savefig('figs/'+filename) 

if not os.path.isdir('figs'): 
    os.makedirs('figs') 
data = np.random.rand(25, 25) 

fig = plt.figure() 
ax = fig.add_axes([0., 0., 1., 1.]) 
# in the real case, imshow is an expensive calculation which can't be put inside the loop 
ax.imshow(data, interpolation='nearest') 

tstart = time.clock() 
for i in range(1, 8): 
    for j in range(3, 13): 
     make_plot(i, j, fig, ax) 

print('took {:.2f} seconds'.format(time.clock()-tstart)) 

回答

2

因爲在這種情況下,限制是調用plt.savefig()它不能優化了不少。在內部,圖形是從頭開始渲染的,這需要一段時間。可能減少要繪製的頂點數量可能會縮短一點時間。

在我的機器上運行代碼的時間(Win 8,i5,4核3.5GHz)爲2.5秒。這似乎不是太糟糕。通過使用多處理可以得到一點改進。

關於多處理的說明:使用multiprocessing中的pyplot狀態機應該可以工作,這似乎令人驚訝。但它確實如此。 在這種情況下,由於每個圖像都基於相同的圖形和軸對象,因此不需要創建新的圖形和座標軸。

我修改一個answer I gave here一段時間以前爲你的情況和使用多處理和對4芯5個處理總時間大致減半。我附加了一個顯示多處理效果的barplot。

import numpy as np 
#import matplotlib as mpl 
#mpl.use('agg') # use of agg seems to slow things down a bit 
import matplotlib.pyplot as plt 
import multiprocessing 
import time, os 

def make_plot(d): 
    start = time.clock() 
    x,y=d 
    #using aspect in this way causes a warning for me 
    #aspect = np.random.random(1)+y/2.0-x 
    xrand = np.random.random(2)*x 
    xlim = [min(xrand), max(xrand)] 
    yrand = np.random.random(2)*y 
    ylim = [min(yrand), max(yrand)] 
    filename = '{:d}_{:d}.png'.format(x,y) 
    ax = plt.gca() 
    #ax.set_aspect(abs(aspect[0])) 
    ax.set_xlim(xlim) 
    ax.set_ylim(ylim) 
    plt.savefig('figs/'+filename) 
    stop = time.clock() 
    return np.array([x,y, start, stop]) 

if not os.path.isdir('figs'): 
    os.makedirs('figs') 
data = np.random.rand(25, 25) 

fig = plt.figure() 
ax = fig.add_axes([0., 0., 1., 1.]) 
ax.imshow(data, interpolation='nearest') 


some_list = [] 
for i in range(1, 8): 
    for j in range(3, 13): 
     some_list.append((i,j)) 


if __name__ == "__main__": 
    multiprocessing.freeze_support() 
    tstart = time.clock() 
    print tstart 
    num_proc = 5 
    p = multiprocessing.Pool(num_proc) 

    nu = p.map(make_plot, some_list) 

    tooktime = 'Plotting of {} frames took {:.2f} seconds' 
    tooktime = tooktime.format(len(some_list), time.clock()-tstart) 
    print tooktime 
    nu = np.array(nu) 

    plt.close("all") 
    fig, ax = plt.subplots(figsize=(8,5)) 
    plt.suptitle(tooktime) 
    ax.barh(np.arange(len(some_list)), nu[:,3]-nu[:,2], 
      height=np.ones(len(some_list)), left=nu[:,2], align="center") 
    ax.set_xlabel("time [s]") 
    ax.set_ylabel("image number") 
    ax.set_ylim([-1,70]) 
    plt.tight_layout() 
    plt.savefig(__file__+".png") 
    plt.show() 

enter image description here