OpenCV 4.12.0
开源计算机视觉
加载中...
搜索中...
无匹配项
性能测量与改进技术

目标

在图像处理中,由于您每秒要处理大量的操作,因此强制要求您的代码不仅要提供正确的解决方案,还要以最快的方式提供解决方案。因此,在本章中,您将学习

除了 OpenCV 之外,Python 还提供了一个模块 time,它有助于测量执行时间。另一个模块 profile 有助于获取代码的详细报告,例如代码中每个函数花费的时间、函数被调用的次数等。但是,如果您使用 IPython,所有这些功能都以用户友好的方式集成在一起。我们将看到一些重要的功能,有关更多详细信息,请查看附加资源部分中的链接。

使用 OpenCV 测量性能

cv.getTickCount 函数返回自参考事件(例如机器打开的时刻)到调用此函数时刻的时钟周期数。因此,如果您在函数执行前后调用它,您将获得执行函数所用的时钟周期数。

cv.getTickFrequency 函数返回时钟周期的频率,即每秒的时钟周期数。因此,要查找以秒为单位的执行时间,您可以执行以下操作

# 您的代码执行
time = (e2 - e1)/ cv.getTickFrequency()
ximgproc.hpp
返回时钟周期数。

我们将用以下示例演示。以下示例应用中值滤波,内核大小为 5 到 49 的奇数。(不要担心结果会是什么样子 - 这不是我们的目标)

img1 = cv.imread('messi5.jpg')
assert img1 is not None, "无法读取文件,请检查 os.path.exists()"
for i in range(5,49,2)
img1 = cv.medianBlur(img1,i)
t = (e2 - e1)/cv.getTickFrequency()
print( t )
# 我得到的结果是 0.521107655 秒
CV_EXPORTS_W Mat imread(const String &filename, int flags=IMREAD_COLOR_BGR)
从文件加载图像。
void medianBlur(InputArray src, OutputArray dst, int ksize)
使用中值滤波器模糊图像。
注意
您可以使用 time 模块做同样的事情。 使用 time.time() 函数代替 cv.getTickCount。 然后取两个时间的差。

OpenCV 中的默认优化

许多 OpenCV 函数都使用 SSE2、AVX 等进行了优化。 它还包含未优化的代码。 因此,如果我们的系统支持这些功能,我们应该利用它们(几乎所有现代处理器都支持它们)。 默认情况下,编译时启用它。 因此,如果启用了 OpenCV,则运行优化的代码,否则运行未优化的代码。 您可以使用 cv.useOptimized() 检查是否已启用/禁用,并使用 cv.setUseOptimized() 来启用/禁用它。 让我们看一个简单的例子。

# 检查是否启用了优化
In [5]: cv.useOptimized()
Out[5]: True
In [6]: %timeit res = cv.medianBlur(img,49)
10 loops, best of 3: 34.9 ms per loop
# 禁用它
In [7]: cv.setUseOptimized(False)
In [8]: cv.useOptimized()
Out[8]: False
In [9]: %timeit res = cv.medianBlur(img,49)
10 loops, best of 3: 64.1 ms per loop
void setUseOptimized(bool onoff)
启用或禁用优化代码。
bool useOptimized()
返回优化代码的使用状态。

如您所见,优化的中值滤波比未优化的版本快 2 倍。 如果您检查其源代码,您会发现中值滤波是 SIMD 优化的。 因此,您可以使用它在代码顶部启用优化(请记住,默认情况下已启用)。

在 IPython 中测量性能

有时您可能需要比较两个类似操作的性能。 IPython 为您提供了一个神奇的命令 timeit 来执行此操作。 它多次运行代码以获得更准确的结果。 再次,它适用于测量单行代码。

例如,您知道以下哪个加法运算更好,x = 5; y = x**2, x = 5; y = x*x, x = np.uint8([5]); y = x*x, 或 y = np.square(x)? 我们将在 IPython shell 中使用 timeit 找出答案。

In [10]: x = 5
In [11]: %timeit y=x**2
10000000 loops, best of 3: 73 ns per loop
In [12]: %timeit y=x*x
10000000 loops, best of 3: 58.3 ns per loop
In [15]: z = np.uint8([5])
In [17]: %timeit y=z*z
1000000 loops, best of 3: 1.25 us per loop
In [19]: %timeit y=np.square(z)
1000000 loops, best of 3: 1.16 us per loop

您可以看到,x = 5 ; y = x*x 最快,并且比 Numpy 快大约 20 倍。 如果您也考虑数组创建,它可能会达到 100 倍的速度。 酷,对吧? (Numpy 开发人员正在解决此问题)

注意
Python 标量运算比 Numpy 标量运算快。 因此,对于包括一个或两个元素的操作,Python 标量比 Numpy 数组更好。 当数组的大小稍大时,Numpy 具有优势。

我们将尝试另一个例子。 这次,我们将比较同一图像的 cv.countNonZero()np.count_nonzero() 的性能。

In [35]: %timeit z = cv.countNonZero(img)
100000 loops, best of 3: 15.8 us per loop
In [36]: %timeit z = np.count_nonzero(img)
1000 loops, best of 3: 370 us per loop
int countNonZero(InputArray src)
Counts non-zero array elements.

参见,OpenCV 函数比 Numpy 函数快近 25 倍。

注意
通常,OpenCV 函数比 Numpy 函数快。 因此,对于相同的操作,首选 OpenCV 函数。 但是,可能存在例外,尤其是在 Numpy 处理视图而不是副本时。

更多 IPython 魔术命令

还有其他几个魔术命令可以测量性能、分析、行分析、内存测量等。它们都有很好的文档记录。 因此,这里只提供指向这些文档的链接。 建议有兴趣的读者尝试一下。

性能优化技术

有几种技术和编码方法可以最大限度地利用 Python 和 Numpy 的性能。 这里只记录了相关的,并给出了重要来源的链接。 这里要注意的主要事情是,首先尝试以简单的方式实现算法。 一旦它工作,分析它,找到瓶颈,并优化它们。

  1. 尽可能避免在 Python 中使用循环,尤其是在双/三循环等中。它们本质上很慢。
  2. 尽可能地向量化算法/代码,因为 Numpy 和 OpenCV 针对向量运算进行了优化。
  3. 利用缓存一致性。
  4. 除非必要,否则不要复制数组。 尽量使用视图代替。 数组复制是一项代价高昂的操作。

如果在完成所有这些操作后,您的代码仍然很慢,或者如果不可避免地需要使用大型循环,请使用 Cython 等其他库来使其更快。

附加资源

  1. Python 优化技术
  2. Scipy 讲义 - 高级 Numpy
  3. IPython 中的计时和分析