在matplotlib中高效绘制多张含多个补丁的图像的方法?
我正在写一段代码,用来显示图片之间的特征匹配。目前这段代码运行得比较慢。我有一些想法可以加快速度,但对matplotlib的使用和它背后的工作原理还不是特别熟悉。
这段代码的基本结构是:(为了让内容更易读,我省略了一些细节)
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和一些手动计算。唯一的缺点是我似乎无法将椭圆的填充设置为false。
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 个回答
我通过手动计算平方根倒数矩阵的形式,成功提高了绘图的效率。这真的让速度快了很多。
在上面的代码中:
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)