繁体   English   中英

有什么方法可以加快此Python代码的速度吗?

[英]Any way to speed up this Python code?

我已经编写了一些Python代码来执行某些图像处理工作,但是运行需要大量时间。 我花了最后几个小时尝试对其进行优化,但是我认为我已经达到了极限。

从探查器的输出看,下面的函数在我的代码的总时间中占了很大的比例。 有什么办法可以加快速度吗?

def make_ellipse(x, x0, y, y0, theta, a, b):
    c = np.cos(theta)
    s = np.sin(theta)
    a2 = a**2
    b2 = b**2
    xnew = x - x0
    ynew = y - y0
    ellipse = (xnew * c + ynew * s)**2/a2 + (xnew * s - ynew * c)**2/b2 <= 1

    return ellipse

为了给出上下文,使用相当大的网格大小的np.meshgrid的输出调用xy ,并将所有其他参数作为简单的整数值进行调用。

尽管该功能似乎要花费很多时间,但可能还有一些方法可以加快其余代码的速度。 我把其余的代码放在了这个要点上

任何想法将不胜感激。 我尝试过使用numba并自动autojit主要功能,但这并没有多大帮助。

让我们尝试结合其调用方优化make_ellipse。

首先,请注意,在多次调用中, ab是相同的。 由于make_ellipse每次都会对它们进行平方,因此只需让调用者执行该操作即可。

其次,请注意np.cos(np.arctan(theta))1 / np.sqrt(1 + theta**2) ,在我的系统上看起来稍快一些。 类似的技巧可用于计算正弦,无论是从theta还是从cos(theta)(或反之亦然)。

第三,不太具体,考虑将一些最终的椭圆公式评估短路。 例如,无论何处(xnew * c + ynew * s)**2/a2大于1,椭圆值必须为False。 如果经常发生这种情况,您可以“屏蔽”这些位置的椭圆(昂贵)计算的后半部分。 我没有彻底地计划这个,但是看一些可能的潜在客户numpy.ma。

它不会在所有情况下都加快处理速度,但是如果您的椭圆不能占据整个图像,则应将对椭圆内点的搜索限制在其边界矩形内。 我对数学很懒,所以我用它搜索了一下,并重复使用了一个反正切技巧的@JohnZwinck整齐余弦来得出这个函数:

def ellipse_bounding_box(x0, y0, theta, a, b):
    x_tan_t = -b * np.tan(theta) /  a
    if np.isinf(x_tan_t) :
        x_cos_t = 0
        x_sin_t = np.sign(x_tan_t)
    else :
        x_cos_t = 1 / np.sqrt(1 + x_tan_t*x_tan_t)
        x_sin_t = x_tan_t * x_cos_t
    x = x0 + a*x_cos_t*np.cos(theta) - b*x_sin_t*np.sin(theta)

    y_tan_t = b / np.tan(theta) /  a
    if np.isinf(y_tan_t):
        y_cos_t = 0
        y_sin_t = np.sign(y_tan_t)
    else:
        y_cos_t = 1 / np.sqrt(1 + y_tan_t*y_tan_t)
        y_sin_t = y_tan_t * y_cos_t
    y = y0 + b*y_sin_t*np.cos(theta) + a*y_cos_t*np.sin(theta)

    return np.sort([-x, x]), np.sort([-y, y])

现在,您可以将原始功能修改为以下形式:

def make_ellipse(x, x0, y, y0, theta, a, b):
    c = np.cos(theta)
    s = np.sin(theta)
    a2 = a**2
    b2 = b**2
    x_box, y_box = ellipse_bounding_box(x0, y0, theta, a, b)
    indices = ((x >= x_box[0]) & (x <= x_box[1]) & 
               (y >= y_box[0]) & (y <= y_box[1]))
    xnew = x[indices] - x0
    ynew = y[indices] - y0
    ellipse = np.zeros_like(x, dtype=np.bool)
    ellipse[indices] = ((xnew * c + ynew * s)**2/a2 +
                        (xnew * s - ynew * c)**2/b2 <= 1)
    return ellipse

由于除x和y之外的所有内容都是整数,因此可以尝试最小化数组计算的数量。 我想大部分时间都花在这个声明上:

ellipse = (xnew * c + ynew * s)**2/a2 + (xnew * s - ynew * c)**2/b2 <= 1

像这样的简单重写应该减少数组操作的数量:

a = float(a)
b = float(b)
ellipse = (xnew * (c/a) + ynew * (s/a))**2 + (xnew * (s/b) - ynew * (c/b))**2 <= 1

现在12阵列操作现在是10(加上4标量运算)。 我不确定numba的jit是否会试过这个。 它可能只是先进行所有广播,然后再执行结果操作。 在这种情况下,重新排序以便立即执行常见操作应该会有所帮助。

继续,你可以再次重写

ellipse = ((xnew + ynew * (s/c)) * (c/a))**2 + ((xnew * (s/c) - ynew) * (c/b))**2 <= 1

要么

t = numpy.tan(theta)
ellipse = ((xnew + ynew * t) * (b/a))**2 + (xnew * t - ynew)**2 <= (b/c)**2

用标量替换另一个数组操作,并消除其他标量操作以获得9个数组操作和2个标量操作。

与往常一样,请注意输入范围以避免舍入错误。

不幸的是,如果两个加数中的任何一个大于比较的右侧,那么就没有办法做好运行总和并提前保释。 这将是一个明显的加速,但你需要cython(或c / c ++)来编码。

使用Cython可以大大加快速度。 有关如何执行此操作的非常好的文档。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM