[英]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
的输出调用x
和y
,并将所有其他参数作为简单的整数值进行调用。
尽管该功能似乎要花费很多时间,但可能还有一些方法可以加快其余代码的速度。 我把其余的代码放在了这个要点上 。
任何想法将不胜感激。 我尝试过使用numba并自动autojit
主要功能,但这并没有多大帮助。
让我们尝试结合其调用方优化make_ellipse。
首先,请注意,在多次调用中, a
和b
是相同的。 由于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.