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


77

我旨在一点一点地启动opencv,但首先我需要确定OpenCV的哪个API更有用。我预测与本地C ++实现相比,Python实现会更短,但运行时间会更密集,更慢。有没有人可以评论这两种观点之间的性能和编码差异?


4
C无论如何,大多数实际工作都是通过OpenCV代码在幕后完成的,因此,只要您自己的代码不太复杂,差异就不会像您天真地期望的那么大。
juanchopanza 2012年

Answers:


161

如先前的回答所述,Python比C ++或C慢。Python的构建是因为其简单性,可移植性以及创造力,用户只需担心他们的算法,而不必担心编程问题。

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

因此,当您从Python调用OpenCV中的函数时,实际运行的是底层C / C ++源代码。因此,性能不会有太大差异。(我记得我读过某个地方的性能损失小于1%,不记得在哪里。OpenCV中一些基本功能的粗略估计显示最坏情况的损失为<4%.ie penalty = [maximum time taken in Python - minimum time taken in C++]/minimum time taken in C++) 。

当您的代码具有大量本地py​​thon代码时,就会出现问题。例如,如果您要创建自己的函数,而这些函数在OpenCV中不可用,那么情况会变得更糟。此类代码在Python中本地运行,从而大大降低了性能。

但是新的OpenCV-Python接口完全支持Numpy。Numpy是使用Python进行科学计算的软件包。它也是本机C代码的包装。它是一个高度优化的库,支持多种矩阵运算,非常适合图像处理。因此,如果您可以正确组合OpenCV函数和Numpy函数,则将获得非常高速的代码。

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

例如,您可以查看以下文章:

  1. Python中的快速数组处理
  2. OpenCV-Python接口,cv和cv2的性能比较

15

所有针对openCV的google结果都相同:python只会稍微慢一点。但我从未见过对此的任何分析。所以我决定做一些发现:

即使使用普通程序,Python也比使用opencv的C ++慢得多。

我能想到的最简单的示例是在屏幕上显示网络摄像头的输出并显示每秒的帧数。使用python,我达到了50FPS(在Intel原子上)。使用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;
        }
    }
}

可能的基准限制:

  • 相机帧率
  • 计时器测量精度
  • 打印格式花费的时间

2
您的测试用例恰好显示了Python和C ++之间的最大差异。因此,这可能不现实。更好的测试方法是看视频帧,然后尝试将自动驾驶汽车的目标瞄准计算机。对于C ++或Python,这几乎是相同的运行时间。不加压力的情况显示了加载帧缓冲区需要多长时间,而没有进行任何实际工作。SO帧加载支配时间。如果进行实际工作,则框抛光仅占总数的2%,而不是总数的100%。
user3150208 '19

1
尽管我目前没有任何基准,但我怀疑它比您预期的重要。例如,如果您在python中运行,dst = cv2.filter2D(img, -1, kernel)则计算机将创建的副本img并将其返回为dst。如果您不使用,img则GC会清理旧图像。openCV python API无法解决此问题。在C / C ++中,您可以轻松创建正确大小的静态图像缓冲区,而不会在每一帧都被创建/销毁。内存分配和释放的时间不为零。
sdfgeoff

将帧频从50增加到65不会提高30%(而不是25%)吗?
AmigoNico

6

sdfgeoff答案缺少您可以在Python中重用数组的事实。预分配它们并传递它们,它们将被使用。所以:

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

据我所知,许多函数(例如filter2D)并不将目标数组作为参数。但是,如果您可以指出一些其他的文档,我将很乐意更改答案。我也很感兴趣看到这种技术的性能比较。
sdfgeoff

2
不知道为什么这么说。这是filter2d的文档:docs.opencv.org/3.4/d4/d86/… 注意,Python中的第四个参数是“ dst”,它是目标数组。我还没有检查过所有地方,但是标准是,如果C ++中有目标arg,那么Python中就存在
Paul Rensing

嗯,是的。我以前没有注意到。我猜我将不得不重做我的性能比较
sdfgeoff

您能否详细说明预分配如何加快流程?我并没有完全理解这一部分,因为对我来说,我似乎只是在不同的位置分配空间,而是以相同的方式分配空间(?)
fogx

预分配的节省来自分配一次数组,然后在循环内调用VideoCapture.read()或filter2d()。一种常见用法是初始化,然后永远循环,从相机读取图像并进行处理。预分配将在每次迭代中节省一毫秒左右的时间。
Paul Rensing

4

没错,Python几乎总是比C ++慢得多,因为它需要解释器,而C ++则不需要。但是,这确实需要对C ++进行强类型化,从而留出了很小的错误余量。有些人喜欢严格地编写代码,而另一些人则喜欢Python固有的宽大处理。

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

编辑:由于Python是一种解释性语言,而C ++被编译为机器代码,通常来说,您可以使用C ++获得性能优势。但是,关于使用OpenCV,核心OpenCV库已经被编译为机器代码,因此围绕OpenCV库的Python包装程序正在执行编译后的代码。换句话说,当涉及到从Python执行计算量大的OpenCV算法时,由于它们已经针对您正在使用的特定体系结构进行了编译,因此不会对性能造成太大影响。


1
是的,python被解释了。但是几乎所有工作都是在openCV内部完成的。假设有20/80的比例。在openCV中完成80%的工作OpenCV是用编译的C编写的。我们正在谈论的是剩下的20%的代码运行的速度。即使Python慢​​4倍,它也只会增加30%的执行时间。ManyopenCV应用程序是5/95分割的,因此Python几乎没有区别
user3150208 '19
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.