【问题标题】:python loops very slowpython循环很慢
【发布时间】:2018-07-04 09:57:46
【问题描述】:

我有一个关于 Python 循环速度的问题。 我创建了以下循环来填充数组中的值,但速度很慢。 有没有办法让它处理得更快?

winW = 1    
winH = 200    
runlength = np.zeros(shape=(img.shape[0], img.shape[1]))


for y in range(0, img.shape[0] - winH, 1):
   for x in range(0, img.shape[1] - winW, 1):
      runlength[y, x] += np.sum(img[y:y + winH, x:x + winW]) / (winH * winW)
      runlength[y + winH, x] += np.sum(img[y:y + winH, x:x + winW]) / (winH * winW)

感谢您的帮助

编辑:我准确地说我只能使用 numpy 而不能使用 scipy

【问题讨论】:

  • 如果您的代码运行没有错误并产生预期的输出,那么在Code Review 上询问可能更合适。代码优化是明确的主题。
  • @Mar:winW一直是1吗??
  • 您可以尝试使用 Cython 的 Numba 进行循环加速,但不一定会有很大帮助,但谁知道呢。
  • @blue_note 是的 winW 总是 1
  • @usr2564301,这种“矢量化”是numpy 在 SO 上最常见的问题之一。 CR 答案倾向于更多地关注风格。但是,如果您是积极的 CR 参与者,并且有贡献的答案...

标签: python performance loops numpy


【解决方案1】:

让我描述一下如何加快for循环中的第一个操作,由

runlength[y, x] += np.sum(img[y:y + winH, x:x + winW]) / (winH * winW)

基本上,您在图像上移动一个宽度为winW 和高度为winH 的矩形。您从图像的点 (0,0) 处的矩形左上角开始,然后将图像中位于该矩形下方的所有值相加,然后将它们除以总点数。位置 (0,0) 的输出就是那个数字。然后将矩形向右移动一个并重复该过程,直到您位于图像的右端。你向下移动一行并重复。

在图像处理方面:您将空间滤镜蒙版应用于图像。该过滤器是宽度winW 和高度winH 的平均过滤器。

要有效地实现这一点,您可以使用scipy.ndimage.correlate 函数。 input 是您的图像,weights 包含乘以矩形下方元素的权重。在这种情况下,这是一个维度为(winH, winW) 的数组,其中每个元素都包含数字1 / (winH * winW)。因此,位于矩形下方的图像的每个点都乘以1 / (winH * winW),然后将所有内容相加。

为了完全匹配您的算法,我们需要将origin 设置为(-np.floor(winH/2), -np.floor(winW/2)) 以指定矩形的均值放置在输出中矩形的右上角位置。

最后,为了完全匹配您的算法,我们必须将(img.shape[0] - winH) 下方或(img.shape[1] - winW) 右侧的所有点设置为零。因此,for循环可以替换为

runlength_corr = correlate(input=img,
                           weights=np.ones((winH, winW)) / (winW * winH),
                           origin=(-np.floor(winH/2), -np.floor(winW/2)))
runlength_corr[(img.shape[0] - winH):, :] = 0
runlength_corr[:, (img.shape[1] - winW):] = 0

我在大小为 512×512 的测试图像上比较了嵌套 for 循环和 correlate 方法的运行时间:

For-loops: Elapsed time: 0.665 sec
Correlate: Elapsed time: 0.085 sec

因此,这提供了 8 倍的良好加速。整个输出的绝对差之和低至 7.04e-09,因此两种方法的输出基本相同。

【讨论】:

  • 非常感谢 hbaderts 的准确回答。问题(很重要,我忘了写)是我不能使用 scipy 来解决我的问题(所以我不能使用卷积函数),只能使用 numpy。
  • 哦,好吧。只要winW 为1,你就可以使用np.correlate
【解决方案2】:

对于初学者,您似乎在循环内部计算了两次相同的数量。仅此一项就可以使您的运行时间减半。

其次,如果winW 总是1,那么np.sum(img[y:y + winH, x:x + winW]) 就是 np.sum(img[y:y + winH, x])。这应该会加快一点。

剩下的就是如何加速np.sum(img[y:y + winH, x])。你可以从计算开始

sum0 = np.sum(img[0: 0 + winH, x])

现在,注意数量

sum1 = np.sum(img[1: 1 + winH, x])

与前一个像素仅相差两个像素,因此,它等于sum0 - img[0, x] + img[1 + winH, x]。下一个y

sum2 = sum1 - img[1, x] + img[2 + winH, x]`

等等

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-31
    相关资源
    最近更新 更多