协调Python Aggdraw中的容器类型以实现最快的渲染?

时间:2014-01-01 23:54:55

标签: python performance rendering sequence aggdraw

原始问题:

我有一个关于Python Aggdraw模块的问题,我在Aggdraw文档中找不到。我正在使用" .polygon"在图像对象上呈现多边形并将输入坐标作为其参数的命令。

我的问题是,是否有人知道或有经验,xy坐标可以在哪些类型的序列容器中(list,tuple,generator,itertools-generator,array,numpy-array,deque等),最重要的是哪个输入类型将帮助Aggdraw以最快的方式渲染图像?

文档只提到多边形方法需要:" Python序列(x,y,x,y,...)"

我认为Aggdraw针对某些序列类型优于其他序列类型,和/或必须首先转换某些序列类型,因此某些类型将比其他类型更快。那么也许有人从理论上或从经验中了解有关Aggdraw内部运作的这些细节?

我已经做了一些初步测试,并且会尽快完成,但我仍然想知道为什么一个选项可能更快的原因,因为可能是我没有正确地进行测试或者还有其他一些方法优化我不知道的Aggdraw渲染。

(顺便说一句,这可能看起来像是微不足道的优化,但当目标是能够快速渲染成千上万的多边形并且能够放大和缩小它们时这不是。所以对于这个问题我不想要建议其他渲染模块(来自我的测试Aggdraw似乎是最快的之一)。我也知道还有其他的优化瓶颈,如坐标到像素的转换等,但是现在我只专注于Aggdraw的最后一步&#39 ; s内部渲染速度。)

非常感谢,很高兴看到其他人的知识和经验与Aggdraw有关。


获胜者?一些初步测试

我现在已经进行了一些初步测试,如果你想要详细信息,请在页面下方的答案中报告结果。主要发现是将浮点坐标舍入到像素坐标作为整数并将它们放在数组中是使Aggdraw渲染图像或贴图的最快方法,并且在可比较的速度下以650%的比例导致令人难以置信的快速渲染加速与众所周知和常用的GIS软件。剩下的就是找到优化坐标转换和shapefile加载的快速方法,这些确实是令人生畏的任务。对于所有调查结果,请在页面下方查看我的答案帖子。

我仍然有兴趣听听您是否已经完成了自己的测试,或者您是否有其他有用的答案或评论。如果有人知道,我仍然对奖金问题的答案感到好奇。


加分问题:

如果你不知道这个问题的具体答案,如果你知道实际的Aggdraw渲染是用哪种编程语言,它可能仍然有用?我读过Aggdraw模块只是原始C ++ Anti-Grain Geometry库的Python绑定,但并不完全确定它实际意味着什么。这是否意味着Aggdraw Python命令只是一种访问和激活c ++库的方式"在幕后"那么实际渲染是用C ++和C ++速度完成的?如果是这样,那么我猜想C ++必须将Python序列转换为C ++序列,并且优化将是找出哪个Python序列可以最快地转换为C ++序列。或者,Aggdraw模块只是用纯Python重写的原始库(因此比C ++版本慢得多)?如果是这样,它支持哪种Python类型,哪种类型的渲染工作更快。 enter code here

1 个答案:

答案 0 :(得分:1)

获胜者?一些初步测试

以下是我初步测试的结果,其中哪些输入类型对于aggdraw渲染更快。在aggdraw文档中可以找到一条线索,它表示aggdraw.polygon()只需要"序列":正式定义为" str,unicode,list,tuple,bytearray,buffer,xrange& #34; (http://docs.python.org/2/library/stdtypes.html)。幸运的是,我发现还有其他输入类型,即aggdraw渲染接受。经过一些测试后,我想出了一个输入容器类型的列表,我可以找到aggdraw(也许还有PIL)渲染支持:

  • 元组
  • 列表
  • 阵列
  • Numpy数组
  • 双端

不幸的是,在提供包含在

中的坐标时,aggdraw不支持并导致错误
  • 发电机
  • itertool generators
  • 字典

然后进行性能测试!测试多边形是来自全球次国家省界的全球行政单位数据库的20 000(多个)多边形的子集,使用PyShp shapefile阅读器模块(http://code.google.com/p/pyshp/)加载到内存中。为了确保测试只测量了aggdraw的内部渲染速度,我确保只有在多边形坐标已经转换为aggdraw图像像素坐标后才启动计时器,并且在我用正确的输入创建了一个输入参数列表之后type和aggdraw.Pen和.Brush对象。然后,我使用带有预加载坐标和参数的itertools.starmap计时并运行渲染:

t=time.time()
iterat = itertools.starmap(draw.polygon, args) #draw is the aggdraw.Draw() object
for runfunc in iterat: #iterating through the itertools generator consumes and runs it
    pass
print time.time()-t

我的发现证实了传统的观念,即元组和数组是最快的Python迭代器,它们最快都是最快的。列表慢了大约50%,numpy阵列也是如此(考虑到Numpy阵列的速度声誉,这最初是令人惊讶的,但后来我读到Numpy阵列只有当它们使用内部Numpy函数时才会很快,而且正常的Python迭代它们通常比其他类型慢)。 Deques,通常被认为是快速的,结果是最慢的(几乎100%,即慢2倍)。

### Coordinates as FLOATS
### Pure rendering time (seconds) for 20 000 polygons from the GADM dataset
tuples
8.90130587328
arrays
9.03419164657
lists
13.424952522
numpy
13.1880489246
deque
16.8887938784

换句话说,如果你经常使用列表作为aggdraw坐标,你应该知道通过将它们放入元组或数组中可以获得50%的性能提升。不是最根本的改进,但仍然有用且易于实施。

但是等等!我确实找到了另一种方法来从aggdraw模块中挤出更多的性能 - 实际上相当多。我忘记了为什么我这样做但是当我尝试将变换后的浮点坐标舍入为最接近的像素整数作为整数类型(即" int(round(eachcoordinate))")在渲染之前我得到了6.5x与最常见的列表容器相比,渲染加速(650%) - 非常值得且易于优化。令人惊讶的是,当渲染器不必担心舍入数字时,阵列容器类型比元组快约25%。这种预处理不会导致我看不到视觉细节的丢失,因为这些浮点无论如何都只能分配给一个像素,这可能是为什么在将坐标发送到aggdraw渲染器之前预先转换/预先围绕坐标会加速进程的原因bc然后aggdraw不必。一个潜在的警告是,取消小数信息可能会改变aggdraw的抗锯齿效果,但在我看来,最终的地图看起来仍然看起来同样抗锯齿和平滑。最后,这个舍入优化必须与在Python中舍入数字所花费的时间进行权衡,但从我可以看到,执行预处理所花费的时间并不会超过渲染加速的好处。应该探索如何以快速方式舍入和转换坐标的进一步优化。

### Coordinates as INTEGERS (rounded to pixels)
### Pure rendering time (seconds) for 20 000 polygons from the GADM dataset
arrays
1.40970077294
tuples
2.19892537074
lists
6.70839555276
numpy
6.47806400659
deque
7.57472232757

总之,那么:数组和元组是使用绘图坐标提供aggdraw(也可能还有PIL?)时使用的最快的容器类型。

考虑到使用带有aggdraw的正确输入类型时可以获得的高渲染速度,即使对地图渲染过程的其他方面进行最轻微的优化也是特别重要和有益的,例如坐标转换例程(我是已经探索并发现Numpy为此目的特别快。)

所有这一点的更普遍的发现是,Python可以用于非常快速的地图渲染应用程序,从而进一步开启了Python地理空间脚本的可能性;例如理论上可以在大约1.5 * 10 = 15秒内渲染20万多个省份的整个GADM数据集,而不考虑坐标到图像坐标转换,这比QGIS甚至ArcGIS更快,在我的经验中,我很难显示GADM数据集。

所有结果均使用Python 2.6.5在8核处理器,2年历史的Windows 7机器上获得。在加载和/或处理数据时,这些结果是否也是最有效的,这个问题必须在另一篇文章中进行测试和回答。如果其他人已经对这些方面有任何好的见解,那将会很有趣。