在matplotlib中使用许多补丁绘制多个图像的有效方法?

时间:2013-02-22 18:48:23

标签: python performance matplotlib

我正在编写显示图像之间匹配功能的代码。目前代码运行速度相当慢。我对如何加快它有一些想法,但我对matplotlib还不是100%舒服,或者它在幕后的工作方式。

代码的基本结构是:(我要留下来让它更具可读性)

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
  • 添加列表图片。每个图像都有自己的轴:ax,并记住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
    
  • 将要素表示可视化为省略号

    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)
    

我已经使用RunSnakeRun对代码进行了分析,而我真正从中得到的就是花费很长时间来绘制所有内容。当我在matplotlib中了解转换时,我所掌握的基本思想是将每个图像绘制在自己的坐标中,然后保持几个转换,以便稍后我可以用它们做很酷的事情,但我怀疑它不会很好地扩展。

平局的实际输出如下:

当我调整大小时,该图需要大约4秒才能重绘,我将要进行平移/缩放。

我为每个功能添加了两个补丁,大约(每个图像300个功能),所以我可以看到一个轮廓和一些透明度。所以,显然有很多开销。但即使没有任何省略号,它也相对较慢。

我还需要编写一些代码来在匹配的特征之间添加线条,但现在我不太确定使用多个轴是一个非常好的主意,特别是当这是一个相对较小的数据集时。

所以,对于更具体的问题:

  • 绘制椭圆与转换圈的效果会更高效吗?使用matplotlib转换的开销是多少?
  • 有没有办法合并一组补丁,以便它们一起转换或更有效?
  • 将所有东西打包成单轴会更有效吗?如果我这样做,仍可以使用变换范式吗?或者转变是这里的罪魁祸首?
  • 是否有快速方法在矩阵列表A上执行sqrtm(inv(A))?或者我是否在for循环中使用它们一样好?
  • 我应该切换到像pyqtgraph这样的东西吗?我不打算在平移和缩放之外做任何动画。 (也许将来我想将这些嵌入到交互式图表中)

编辑:

我已经能够通过手动计算平方根倒置矩阵的形式来提高绘图效率。它的速度也相当快。

在上面的代码中:

 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) )

替换为

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

我也发现了一些有趣的时间结果:

  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) 

编辑2

我已经使用PatchCollection和一些手动计算得到了很快计算和绘制的椭圆(在大约一秒钟内,我没有对它进行分析)。 唯一的缺点是我似乎无法将椭圆的填充设置为假

 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)

1 个答案:

答案 0 :(得分:0)

我已经能够通过手动计算平方根倒置矩阵的形式来提高绘图效率。它的速度也相当快。

在上面的代码中:

 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) )

替换为

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

我也发现了一些有趣的时间结果:

  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) 

编辑2

我已经使用PatchCollection和一些手动计算得到了很快计算和绘制的椭圆(在大约一秒钟内,我没有对它进行分析)。 唯一的缺点是我似乎无法将椭圆的填充设置为假

 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)