2013-02-22 8 views
5

Sto scrivendo un codice che mostra le corrispondenze delle funzioni tra le immagini. Il codice funziona piuttosto lentamente al momento. Ho alcune idee su come accelerarlo, ma non sono al 100% a mio agio con Matplotlib o su come funziona dietro le quinte.Un modo efficiente per tracciare più immagini con molte patch in matplotlib?

La struttura di base del codice è: (Sto lasciando le cose per renderlo più leggibile)

from matplotlib.patches import Rectangle, Circle, Ellipse 
import matplotlib.gridspec as gridspec 
from matplotlib.transforms import Affine2D 
from scipy.linalg import inv, sqrtm 
import matplotlib.pyplot as plt 
import numpy as np 
  • aggiungere un elenco immagini. Ogni immagine ottiene i suoi propri assi: ascia, e ricorda ax.transData

    gs = gridspec.GridSpec(nr, nc) 
    for i in range(num_images): 
        dm.ax_list[i] = plt.subplot(gs[i]) 
        dm.ax_list[i].imshow(img_list[i]) 
        transData_list[i] = dm.ax_list[i].transData 
    
  • visualizzare la rappresentazione caratteristica come ellissi

    for i in range(num_chips): 
        axi = chips[i].axi 
        ax = dm.ax_list[axi] 
        transData = dm.transData_list[axi] 
        chip_feats = chips[i].features 
        for feat in chip_feats: 
         (x,y,a,c,d) = feat 
         A = numpy.array([ (a, 0, 0) , 
                (c, d, 0) , 
                (0, 0, 1) ] , dtype=np.float64) 
         EllShape = Affine2D(numpy.array(sqrtm(inv(A)), dtype=np.float64)) 
         transEll = EllShape.translate(x,y) 
         unitCirc = Circle((0,0),1,transform=transEll+transData) 
         ax.add_patch(unitCirc) 
    

ho usato RunSnakeRun al profilo del codice, e tutti Capisco davvero da quello che ci vuole molto tempo per disegnare tutto. L'idea di base che ho avuto quando ho saputo della trasformazione in matplotlib è stata quella di disegnare ogni immagine nelle sue coordinate, e quindi mantenere diverse trasformazioni in modo da poter fare cose interessanti con loro più tardi, ma sospetto che non stia andando bene.

L'uscita effettiva del sorteggio si presenta così:

La cifra è di circa 4 secondi per ridisegnare quando ho ridimensionare, e ho intenzione di mancare di pan/zoom.

Aggiungevo due patch per ogni funzione e circa (300 funzioni per immagine) in modo da poter vedere un contorno e un po 'di trasparenza. Quindi, ovviamente c'è un sovraccarico per questo. Ma è anche relativamente lento anche senza ellissi.

Ho anche bisogno di scrivere del codice per mettere le linee tra le funzionalità di corrispondenza, ma ora non sono così sicuro che l'uso di più assi è un'ottima idea, specialmente quando si tratta di un dataset relativamente piccolo.

Così, per ulteriori domande concrete:

  • sarebbe più efficace per tracciare ellissi vs Circles trasformati? Qual è il sovraccarico dell'utilizzo delle trasformazioni di matplotlib?
  • C'è un modo per combinare un gruppo di patch in modo che possano trasformarsi insieme o in modo più efficiente?
  • Sarebbe più efficiente mettere tutto in un unico asse? Il paradigma delle trasformazioni può ancora essere usato se lo faccio? O è la trasformazione il colpevole principale qui?
  • C'è un modo rapido per eseguire sqrtm (inv (A)) in un elenco di matrici A? O è altrettanto che li ho in un ciclo for?
  • Devo passare a qualcosa come pyqtgraph? Non sto pensando di essere un'animazione al di là di pan e zoom.(Magari in futuro avrò voglia di incorporare questi in un grafico interattivo)

Modifiche:

sono stato in grado di aumentare l'efficienza disegno calcolando la forma della radice quadrata invertito matrice a mano. Anche una grande accelerazione.

Nel codice precedente:

A = numpy.array([ (a, 0, 0) , 
          (c, d, 0) , 
          (0, 0, 1) ] , dtype=np.float64) 
EllShape = Affine2D(numpy.array(sqrtm(inv(A)), dtype=np.float64)) 

è sostituito da

EllShape = Affine2D([\ 
(1/sqrt(a),   0, 0),\ 
((c/sqrt(a) - c/sqrt(d))/(a - d), 1/sqrt(d), 0),\ 
(  0,   0, 1)]) 

ho trovato alcuni tempi interessanti risultati anche:

num_to_run = 100000 
    all_setup = ''' import numpy as np ; from scipy.linalg import sqrtm ; from numpy.linalg import inv ; from numpy import sqrt 
    a=.1 ; c=43.2 ; d=32.343''' 

    timeit(\ 
    'sqrtm(inv(np.array([ (a, 0, 0) , (c, d, 0) , (0, 0, 1) ])))',\ 
    setup=all_setup, number=num_to_run) 
    >> 22.2588094075 #(Matlab reports 8 seconds for this run) 

    timeit(\ 
    '[ (1/sqrt(a), 0, 0), ((c/sqrt(a) - c/sqrt(d))/(a - d), 1/sqrt(d), 0), (0, 0, 1) ]',\ 
    setup=all_setup, number=num_to_run) 
    >> 1.10265190941 #(Matlab reports .1 seconds for this run) 

EDIT 2

Ive ha ottenuto le ellissi da calcolare e disegnare molto rapidamente (in circa un secondo, non l'ho tracciato) utilizzando un PatchCollection e alcuni calcoli manuali. L'unico inconveniente è che non riesco a impostare il riempimento delle ellissi è falso

from matplotlib.collections import PatchCollection 
ell_list = [] 
for i in range(num_chips): 
    axi = chips[i].axi 
    ax = dm.ax_list[axi] 
    transData = dm.transData_list[axi] 
    chip_feats = chips[i].features 
    for feat in chip_feats: 
     (x,y,a,c,d) = feat 
     EllShape = Affine2D([\ 
      (1/sqrt(a),   0, x),\ 
      ((c/sqrt(a) - c/sqrt(d))/(a - d), 1/sqrt(d), y),\ 
      (  0,   0, 1)]) 
     unitCirc = Circle((0,0),1,transform=EllShape) 
     ell_list = [unitCirc] + ell_list 
    ellipses = PatchCollection(ell_list) 
    ellipses.set_color([1,1,1]) 
    ellipses.face_color('none') #'none' gives no fill, while None will default to [0,0,1] 
    ellipses.set_alpha(.05) 
    ellipses.set_transformation(transData) 
    ax.add_collection(ellipses) 
+0

hai bisogno per essere in grado di modificare le patch in modo indipendente dopo averli disegnati? – tacaswell

+0

Gli ellissi non avranno bisogno di cambiare forma dopo che sono stati disegnati sulla trama. Possono scalare o trasformarsi con la trama, ma non indipendentemente l'una dall'altra. Sto trasformando i cerchi unitari in ellissi perché è più facile che cercare di ottenere xscale, yscale e rotazione dalla matrice. – Erotemic

+3

Potrebbe essere possibile inserire tutte le ellissi in una raccolta di patch che potrebbe velocizzare il disegno. – tacaswell

risposta

0

Sono stato in grado di aumentare l'efficienza disegno calcolando la forma della radice quadrata matrice invertita a mano. Anche una grande accelerazione.

Nel codice precedente:

A = numpy.array([ (a, 0, 0) , 
          (c, d, 0) , 
          (0, 0, 1) ] , dtype=np.float64) 
EllShape = Affine2D(numpy.array(sqrtm(inv(A)), dtype=np.float64)) 

è sostituito da

EllShape = Affine2D([\ 
(1/sqrt(a),   0, 0),\ 
((c/sqrt(a) - c/sqrt(d))/(a - d), 1/sqrt(d), 0),\ 
(  0,   0, 1)]) 

ho trovato alcuni tempi interessanti risultati anche:

num_to_run = 100000 
    all_setup = ''' import numpy as np ; from scipy.linalg import sqrtm ; from numpy.linalg import inv ; from numpy import sqrt 
    a=.1 ; c=43.2 ; d=32.343''' 

    timeit(\ 
    'sqrtm(inv(np.array([ (a, 0, 0) , (c, d, 0) , (0, 0, 1) ])))',\ 
    setup=all_setup, number=num_to_run) 
    >> 22.2588094075 #(Matlab reports 8 seconds for this run) 

    timeit(\ 
    '[ (1/sqrt(a), 0, 0), ((c/sqrt(a) - c/sqrt(d))/(a - d), 1/sqrt(d), 0), (0, 0, 1) ]',\ 
    setup=all_setup, number=num_to_run) 
    >> 1.10265190941 #(Matlab reports .1 seconds for this run) 

EDIT 2

Ive ha ottenuto le ellissi da calcolare e disegnare molto rapidamente (in circa un secondo, non l'ho tracciato) utilizzando un PatchCollection e alcuni calcoli manuali. L'unico inconveniente è che non riesco a impostare il riempimento delle ellissi di essere falsa

from matplotlib.collections import PatchCollection 
ell_list = [] 
for i in range(num_chips): 
    axi = chips[i].axi 
    ax = dm.ax_list[axi] 
    transData = dm.transData_list[axi] 
    chip_feats = chips[i].features 
    for feat in chip_feats: 
     (x,y,a,c,d) = feat 
     EllShape = Affine2D([\ 
      (1/sqrt(a),   0, x),\ 
      ((c/sqrt(a) - c/sqrt(d))/(a - d), 1/sqrt(d), y),\ 
      (  0,   0, 1)]) 
     unitCirc = Circle((0,0),1,transform=EllShape) 
     ell_list = [unitCirc] + ell_list 
    ellipses = PatchCollection(ell_list) 
    ellipses.set_color([1,1,1]) 
    ellipses.face_color('none') #'none' gives no fill, while None will default to [0,0,1] 
    ellipses.set_alpha(.05) 
    ellipses.set_transformation(transData) 
    ax.add_collection(ellipses) 
Problemi correlati