1

原始问题:

我对在 Aggdraw 文档中找不到的 Python Aggdraw 模块有疑问。我正在使用“.polygon”命令在图像对象上渲染多边形并将输入坐标作为其参数。

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

文档只提到多边形方法采用:“A Python sequence (x, y, x, y, ...)”

我认为 Aggdraw 针对某些序列类型比其他类型更优化,和/或某些序列类型必须首先转换,因此某些类型会比其他类型更快。所以也许有人知道关于 Aggdraw 内部运作的这些细节,无论是理论上还是经验上?

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

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

非常感谢,很想知道其他人对 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

4

1 回答 1

1

赢家?一些初步测试

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

  • 元组
  • 列表
  • 数组
  • numpy 数组
  • 双端队列

不幸的是,aggdraw 不支持并在提供包含在以下内容中的坐标时导致错误:

  • 发电机
  • 迭代工具生成器
  • 字典

然后进行性能测试!测试多边形是来自全球次国家省边界的全球行政单位数据库的 20 000 个(多)多边形的子集,使用 PyShp shapefile 读取器模块(http://code.google.com/p/pyshp )加载到内存中/)。为了确保测试只测量 aggdraw 的内部渲染速度,我确保仅在多边形坐标已经转换为 aggdraw 图像像素坐标之后,并且在我创建了具有正确输入类型和 aggdraw 的输入参数列表之后才启动计时器。笔和 .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 迭代,它们通常比其他类型慢)。双端队列,通常被认为是快的,结果却是最慢的(几乎 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.5 倍的渲染加速(650%)与最常见的列表容器相比——非常值得且易于优化。令人惊讶的是,当渲染器不必担心舍入数字时,数组容器类型比元组快约 25%。这种预舍入不会丢失我可以看到的视觉细节,因为这些浮点无论如何只能分配给一个像素,这可能是为什么在将坐标发送到 aggdraw 渲染器之前对坐标进行预转换/预舍入加快了进程 bc 而 aggdraw 不必这样做的原因。一个潜在的警告是,删除十进制信息可能会改变 aggdraw 进行抗锯齿的方式,但在我看来,最终的地图仍然看起来同样抗锯齿和平滑。最后,必须权衡这种舍入优化与在 Python 中舍入数字所需的时间,但据我所知,进行预舍入所需的时间并没有超过渲染加速的好处。应该进一步优化如何以快速的方式对坐标进行四舍五入和转换。一个潜在的警告是,删除十进制信息可能会改变 aggdraw 进行抗锯齿的方式,但在我看来,最终的地图仍然看起来同样抗锯齿和平滑。最后,必须权衡这种舍入优化与在 Python 中舍入数字所需的时间,但据我所知,进行预舍入所需的时间并没有超过渲染加速的好处。应该进一步优化如何以快速的方式对坐标进行四舍五入和转换。一个潜在的警告是,删除十进制信息可能会改变 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 秒内渲染 200 000 多个省份的整个 GADM 数据集,而无需考虑坐标到图像的坐标转换,这比 QGIS 甚至 ArcGIS 快得多,根据我的经验,它在显示 GADM 数据集方面遇到了困难.

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

于 2014-01-03T01:51:47.107 回答