Note that there are some explanatory texts on larger screens.

plurals
  1. POEfficient way to plotting multiple images with many patches in matplotlib?
    primarykey
    data
    text
    <p>I'm writing code which displays features matches between images. The code runs fairly slow at the moment. I have some ideas on how to speed it up, but I'm not 100% comfortable with matplotlib yet or how its working behind the scenes. </p> <p>The basic structure of the code is: (I'm leaving things out to make it more readable) </p> <pre><code>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 </code></pre> <ul> <li><p>Add a list images. Each image gets its own axes: ax, and remembers ax.transData</p> <pre><code>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 </code></pre></li> <li><p>Visualize the feature representation as Ellipses</p> <pre><code>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) </code></pre></li> </ul> <p>I've used RunSnakeRun to profile the code, and all I really gather from that is it is taking a long time to draw everything. The basic idea that I had when I learned about the transformation in matplotlib was to draw each image in its own coordinates, and then maintain several transformations so I could do cool things with them later, but I suspect that it's not going to scale well. </p> <p>The actual output of the draw looks like this: </p> <p><img src="https://i.imgur.com/TeMrXv5.png" alt=""></p> <p>The figure takes about 4 seconds to redraw when I resize it, and I'm going to be wanting to pan/zoom. </p> <p>I was adding two patches for each feature, and about (300 features per image) so I could see an outline and some transparency. So, there's obviously overhead for that. But its also relatively slow even without any ellipses. </p> <p>I also need to write some code to put lines in between the matching features, but now I'm not so sure using multiple axes is a very good idea, especially when this is a relatively small dataset. </p> <p>So, for more concrete questions: </p> <ul> <li>Would it be more efficient to plot Ellipses vs Transformed Circles? What is the overhead of using matplotlib transformations? </li> <li>Is there a way to combine a group of patches so they transform together or more efficiently? </li> <li>Would it be more efficient to pack everything into a single axes? Can the paradigm of transforms still be used if I do that? Or is the transform the main culprit here?</li> <li>Is there a quick way to do the sqrtm( inv( A ) ) on a list of matrices A? Or is it just as well that I have them in a for loop? </li> <li>Should I switch to something like pyqtgraph? I'm not planning on their being any animation beyond pan and zoom. ( maybe in the future I'll want to embed these into a interactive graph )</li> </ul> <hr> <p>EDITS: </p> <p>I've been able to increase drawing efficiency by computing the form of the square root inverted matrix by hand. Its a pretty big speed up too. </p> <p>In the above code: </p> <pre><code> 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) ) </code></pre> <p>is replaced by </p> <pre><code> EllShape = Affine2D([\ ( 1/sqrt(a), 0, 0),\ ((c/sqrt(a) - c/sqrt(d))/(a - d), 1/sqrt(d), 0),\ ( 0, 0, 1)]) </code></pre> <p>I found some interesting timing results too: </p> <pre><code> 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) &gt;&gt; 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) &gt;&gt; 1.10265190941 #(Matlab reports .1 seconds for this run) </code></pre> <hr> <p>EDIT 2</p> <p>Ive gotten the ellipses to be computed and drawn very quickly (in about a second, I didn't profile it) using a PatchCollection and some manual calculations. The only drawback is I can't seem to set the fill of the ellipses to be false</p> <pre><code> 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) </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload