OpenCV 的 Python 或 C++ 编码之间的性能是不是不同?

Posted

技术标签:

【中文标题】OpenCV 的 Python 或 C++ 编码之间的性能是不是不同?【英文标题】:Does performance differ between Python or C++ coding of OpenCV?OpenCV 的 Python 或 C++ 编码之间的性能是否不同? 【发布时间】:2012-11-06 03:14:48 【问题描述】:

我的目标是一点一点地启动 opencv,但首先我需要确定 OpenCV 的哪个 API 更有用。我预测 Python 实现会更短,但与原生 C++ 实现相比,运行时间会更密集且更慢。有知道的可以评论一下这两个角度之间的性能和编码差异吗?

【问题讨论】:

大部分实际工作都是由 OpenCV C 代码在幕后完成的,因此只要您自己的代码不太复杂,差异不应像您天真期望的那么大. 【参考方案1】:

如前面的答案所述,与 C++ 或 C 相比,Python 速度较慢。Python 的构建是为了其简单性、可移植性以及创造性,用户只需担心他们的算法,而不是编程问题。

但在 OpenCV 中,有些不同。 Python-OpenCV 只是原始 C/C++ 代码的包装器。它通常用于结合两种语言的最佳特性,C/C++ 的性能和 Python 的简单性

因此,当您从 Python 调用 OpenCV 中的函数时,实际运行的是底层 C/C++ 源代码。所以性能上不会有太大差异。(我记得我在某处读到过性能损失最坏情况强> 罚款<4%。即penalty = [maximum time taken in Python - minimum time taken in C++]/minimum time taken in C++)。

当您的代码有很多本机 python 代码时,就会出现问题。例如,如果您正在制作自己的函数,而这些函数在 OpenCV 中不可用,那么情况会变得更糟。此类代码在 Python 中本地运行,这大大降低了性能。

但是新的 OpenCV-Python 接口完全支持 Numpy。 Numpy 是 Python 中用于科学计算的包。它也是原生 C 代码的包装器。它是一个高度优化的库,支持多种矩阵运算,非常适合图像处理。所以如果你能正确结合 OpenCV 函数和 Numpy 函数,你会得到一个非常高速的代码。

要记住的是,始终尽量避免 Python 中的循环和迭代。相反,使用 Numpy(和 OpenCV)中可用的数组操作工具。使用 C = A+B 简单地添加两个 numpy 数组比使用双循环快很多倍。

例如,您可以查看这些文章:

    Fast Array Manipulation in Python Performance comparison of OpenCV-Python interfaces, cv and cv2

【讨论】:

【参考方案2】:

openCV 的所有谷歌结果都是一样的:python 只会稍微慢一点。但我一次也没有看到任何关于此的分析。所以我决定做一些并发现:

Python 比使用 opencv 的 C++ 慢得多,即使对于琐碎的程序也是如此。

我能想到的最简单的例子是在屏幕上显示网络摄像头的输出并显示每秒的帧数。使用 python,我达到了 50FPS(在 Intel atom 上)。使用 C++,我得到了 65FPS,增加了 25%。在这两种情况下,CPU 使用率都使用单核,据我所知,受 CPU 性能的限制。 此外,这个测试用例与我在过去从一个移植到另一个的项目中看到的一致。

这种差异从何而来?在 python 中,所有的 openCV 函数都返回图像矩阵的新副本。每当您捕获图像或调整图像大小时 - 在 C++ 中,您都可以重用现有内存。在python中你不能。我怀疑这次分配内存所花费的时间是主要区别,因为正如其他人所说:openCV 的底层代码是 C++。

在你把 python 扔出窗外之前:python 的开发速度要快得多,如果你没有遇到硬件限制,或者如果开发速度比性能更重要,那么使用 python。在我使用openCV 完成的许多应用程序中,我从python 开始,后来只将计算机视觉组件转换为C++(例如,使用python 的ctype 模块并将CV 代码编译到共享库中)。

Python 代码:

import cv2
import time

FPS_SMOOTHING = 0.9

cap = cv2.VideoCapture(2)
fps = 0.0
prev = time.time()
while True:
    now = time.time()
    fps = (fps*FPS_SMOOTHING + (1/(now - prev))*(1.0 - FPS_SMOOTHING))
    prev = now

    print("fps: :.1f".format(fps))

    got, frame = cap.read()
    if got:
        cv2.imshow("asdf", frame)
    if (cv2.waitKey(2) == 27):
        break

C++ 代码:

#include <opencv2/opencv.hpp>
#include <stdint.h>

using namespace std;
using namespace cv;

#define FPS_SMOOTHING 0.9

int main(int argc, char** argv)
    VideoCapture cap(2);
    Mat frame;

    float fps = 0.0;
    double prev = clock(); 
    while (true)
        double now = (clock()/(double)CLOCKS_PER_SEC);
        fps = (fps*FPS_SMOOTHING + (1/(now - prev))*(1.0 - FPS_SMOOTHING));
        prev = now;

        printf("fps: %.1f\n", fps);

        if (cap.isOpened())
            cap.read(frame);
        
        imshow("asdf", frame);
        if (waitKey(2) == 27)
            break;
        
    

可能的基准限制:

相机帧率 定时器测量精度 花在打印格式上的时间

【讨论】:

您的测试用例恰好是显示 Python 和 C++ 之间最大差异的测试用例。所以可能不太现实。更好的测试是查看视频帧,并尝试计算自动驾驶汽车的瞄准目标。这与 C++ 或 Python 的运行时间几乎相同。无新闻案例显示加载帧缓冲区需要多长时间,而不是任何实际工作。 SO 帧加载占主导地位。如果做实际工作,那么帧缓冲只占总数的 2%,而不是总数的 100%。 虽然我目前没有任何基准,但我怀疑它比您预测的更重要。例如,如果您在 python 中运行dst = cv2.filter2D(img, -1, kernel),那么计算机会创建img 的副本并将其返回为dst。如果你不使用img,那么 GC 会来清理旧图像。使用 openCV python API 无法解决这个问题。在 C/C++ 中,您可以轻松创建大小正确的静态图像缓冲区,而不会在每一帧中创建/销毁。内存分配和释放的时间不为零。 帧速率从 50 增加到 65 不是提高 30%(而不是 25%)吗?【参考方案3】:

没错,Python 几乎总是比 C++ 慢很多,因为它需要一个解释器,而 C++ 不需要。但是,这确实需要 C++ 是强类型的,这留下了更小的错误余地。有些人喜欢被要求严格编码,而另一些人则喜欢 Python 与生俱来的宽容。

如果您想全面了解 Python 编码风格与 C++ 编码风格,这不是最好的地方,请尝试查找文章。

编辑: 因为 Python 是一种解释型语言,而 C++ 被编译为机器码,一般来说,使用 C++ 可以获得性能优势。然而,关于使用 OpenCV,核心 OpenCV 库已经编译为机器代码,因此 OpenCV 库周围的 Python 包装器正在执行编译后的代码。换句话说,当涉及到从 Python 执行计算成本高昂的 OpenCV 算法时,您不会看到太多的性能损失,因为它们已经针对您正在使用的特定架构进行了编译。

【讨论】:

是的,python 被解释了。但是几乎所有的工作都是在 openCV 内部完成的。假设有一个 20/80 的拆分。在 openCV 中完成了 80% 的工作 OpenCV 是用编译后的 C 编写的。我们正在谈论的是剩余 20% 的代码运行速度有多快。即使 Python 慢了 4 倍,它也只会增加 30% 的执行时间。许多openCV 应用程序是 5/95 拆分的,因此 Python 几乎没有区别【参考方案4】:

answer from sdfgeoff 缺少您可以在 Python 中重用数组的事实。预先分配它们并传递它们,它们就会被使用。所以:

    image = numpy.zeros(shape=(height, width, 3), dtype=numpy.uint8)
    #....
    retval, _ = cv.VideoCapture.read(image)

【讨论】:

据我所知,许多函数(例如 filter2D)不将目标数组作为参数。但是,如果您可以向我指出一些另有说明的文档,我会很乐意改变我的答案。我也很想看看与这种技术的性能比较。 不知道你为什么这么说。这是 filter2d 的文档:docs.opencv.org/3.4/d4/d86/… 注意 Python 中的第四个参数是“dst”,它是目标数组。我没有到处检查,但标准是如果 C++ 中有目标 arg,那么它在 Python 中有 嗯,你是对的。我以前没有注意到这一点。我想我得重新进行性能比较 您能否详细说明预分配如何加快流程?我没有完全理解那部分,因为对我来说,看起来我只是在不同的点分配空间,但分配都是一样的(?) 预分配的节省来自分配数组一次,然后在循环内调用 VideoCapture.read() 或 filter2d()。一个常见的用法可能是初始化,然后永远循环,从相机读取图像并处理它。预分配将在每次迭代中节省一毫秒左右的时间。【参考方案5】:

为什么选择? 如果您同时知道PythonC++,请使用Python 进行研究,使用Jupyter Notebooks,然后使用C++ 进行实施。 Python 堆栈 JupyterOpenCV (cv2)Numpy 提供快速原型设计。 将代码移植到C++ 通常非常简单。

【讨论】:

是的!我将补充一点,它还取决于您的最终应用程序目标。只要满足最终产品的要求,就可以继续使用 Python。另一种情况可能是 Python 不可用(例如嵌入式板,...)

以上是关于OpenCV 的 Python 或 C++ 编码之间的性能是不是不同?的主要内容,如果未能解决你的问题,请参考以下文章

Python环境搭建之OpenCV

c++ opencv获取编码的网络摄像头流

C++编码优化之减少冗余拷贝或赋值

C++编码优化之减少冗余拷贝或赋值

C++编码优化之减少冗余拷贝或赋值

opencv系列之基于NVIDIA显卡的opencv-python硬解方案