OpenCV:C++和C性能比较

17

我现在正在使用OpenCV API(C ++)开发一些处理视频的应用程序。

在PC上,一切都非常快。今天我决定将这个应用程序移植到Android设备上(使用相机作为视频输入)。幸运的是,有适用于Android的OpenCV,所以我只需将本机代码添加到示例Android应用程序中即可。除了性能外,一切都很好。我对我的应用程序进行了基准测试,发现它的帧速率只有4-5 fps,这实际上是不能接受的(我的设备只有单核1ghz处理器),我想它应该以大约10 fps的速度运行。

重新用C完全重写我的应用程序有意义吗?我知道使用诸如std :: vector之类的东西对开发人员来说更加方便,但我不在乎。

似乎OpenCV C接口具有与C ++接口相同的功能/方法。

我谷歌了这个问题,但没有找到任何答案。

感谢您的任何建议。


3
我不特别熟悉Android,但是使用OpenCV的C接口并不会给你带来明显的性能提升,因为它仍然使用相同的基础代码。 - Mohammad
我在我的回答中添加了一些更多的信息,希望你会发现它有用。 - Sam
7个回答

56
我在Android和优化方面做了很多工作(我写了一个视频处理应用程序,可以在4毫秒内处理一帧),因此我希望我能给你提供一些相关的答案。
OpenCV中的C和C++接口之间没有太大的区别。部分代码是用C编写的,并有C++包装器,反之亦然。根据Shervin Emami的测量,两者之间的任何显着差异都是回归、错误修复或质量改进。您应该使用最新的OpenCV版本。
为什么不重写?
您将花费大量时间,而这些时间可以更好地利用。C接口很麻烦,引入错误或内存泄漏的机会很高。在我看来,您应该避免使用它。
优化建议
A. 开启优化。
编译器优化和缺少调试断言都可以在运行时产生很大的差异。
B. 对您的应用程序进行性能分析。

首先在电脑上进行操作,因为这样更容易。使用Visual Studio Profiler来识别缓慢的部分。对它们进行优化。不要因为你认为它很慢就进行优化,而是因为你测量了它的速度。从最慢的函数开始,尽可能地进行优化,然后再处理第二慢的函数。测量您的更改以确保它确实更快。

C. 关注算法。

更快的算法可以使性能提高数个数量级(100倍)。C++技巧可能会让您的性能提高2倍。

经典技术:

  • 将视频帧调整为较小的尺寸。通常情况下,您可以从200x300像素图像中提取信息,而不是1024x768像素的图像。第一种图像的面积要小10倍。

  • 使用简单的操作代替复杂的操作。使用整数而不是浮点数。在执行数千次的矩阵或for循环中永远不要使用double

  • 尽可能少地计算。您能否仅在图像的特定区域跟踪对象,而不是为所有帧处理它?您能否对非常小的图像进行粗略/近似检测,然后在完整帧中的ROI上进行细化?

D. 在关键部分使用C语言

在循环中,使用C语言风格而不是C ++可能更有意义。数据矩阵或浮点数组的指针比mat.at或std :: vector<>快得多。通常瓶颈在于嵌套循环。专注于此。没有必要在代码中到处替换vector<>并使其变得混乱。

E. 避免隐藏成本

一些OpenCV函数会将数据转换为double类型进行处理,然后再转换回输入格式。这些函数会严重影响移动设备的性能,如:warping、scaling和类型转换等。此外,色彩空间转换也会导致性能下降。建议使用从本机YUV直接获取的灰度图像。
使用向量化技术可以提高ARM处理器的性能,其中NEON是一种常用的技术。学习并掌握它,它非常强大!
以下是一个小例子:
float* a, *b, *c;
// init a and b to 1000001 elements
for(int i=0;i<1000001;i++)
    c[i] = a[i]*b[i];

可以改写为以下形式。这种方式更冗长,但速度更快。
float* a, *b, *c;
// init a and b to 1000001 elements
float32x4_t _a, _b, _c;
int i;
for(i=0;i<1000001;i+=4)
{  
    a_ = vld1q_f32( &a[i] ); // load 4 floats from a in a NEON register
    b_ = vld1q_f32( &b[i] );
    c_ = vmulq_f32(a_, b_); // perform 4 float multiplies in parrallel
    vst1q_f32( &c[i], c_); // store the four results in c
}
// the vector size is not always multiple of 4 or 8 or 16. 
// Process the remaining elements
for(;i<1000001;i++)
    c[i] = a[i]*b[i];

纯粹主义者认为你必须用汇编语言编写,但对于普通程序员来说,这有点令人生畏。我使用gcc内置函数获得了良好的结果,就像上面的例子一样。
另一个快速启动的方法是将手写的SSE优化代码转换为NEON。 SSE是英特尔处理器中NEON的等效物,并且许多OpenCV函数使用它,例如这里。这是uchar矩阵(常规图像格式)的图像过滤代码。您不应该盲目地逐个转换指令,而是以此作为开始的示例。
您可以在这个博客和以下帖子中了解更多关于NEON的信息。 G.注意图像捕获。

在移动设备上,速度可能会非常慢。优化需要根据设备和操作系统进行。


你有关于如何轮询相机的建议吗?在 Nexus 4 上使用 OpenCV 2.4.3.2 的示例,注释掉任何处理,我只能看到大多数分辨率下的10fps,如果我降到176x144,则为20fps... - Kaolin Fire
2
不要使用OpenCV相机API。使用Java API捕获帧,并将它们传递给本地代码。 - Sam
谢谢!我在考虑是否需要这样做,调整类似于iOS中必须完成的工作。你会认为至少有一个OpenCV示例是专门针对吞吐量的... - Kaolin Fire

6
在做出此类决定之前,您应该对代码进行分析以定位热点。如果没有这些信息,那么您对速度进行的任何更改都将是瞎猜。您尝试过Android NDK分析器吗?

2
@Astor 如果那不起作用,你总可以使用旧技巧,在调试器中反复暂停应用程序,以了解它大部分时间都在哪里花费。 - Alex Wilson

4

Shervin Imami在他的网站上进行了一些性能测试。您可以查看它以获取一些想法。

http://www.shervinemami.info/timingTests.html

希望这有所帮助。

(如果您找到任何性能提升的方法,也可以在某个地方分享您自己的发现,那将是很好的。)


谢谢您的回复 - 我会查看那个测试,也许会创建自己的。 - ArtemStorozhuk

3
我在多次测试中发现:
  1. 当直接访问像素时,C接口(IplImage)比使用Mat.at(x,y)方法快数倍。当我将我的C++应用程序转换为C时,在blob检测例程中获得了3倍的性能提升。

  2. 在外部应用程序(例如LabView)调用某些例程时,C++接口会崩溃,而在C中调用相同的例程时可以正常工作。其中一个例子是FindContours和cvFindContours。

  3. C更适用于嵌入式设备。但是,我在这个领域还没有做过任何事情。


3
我猜这个问题需要这样表述:C比C++更快吗?答案是否定的。两者都编译成本地机器语言,而C++被设计为与C一样快。 至于STL(尤其是ISO标准),也被设计和注意到它们与指针一样快,同时提供了灵活性。 唯一使用C的原因是您的平台不支持C++。 在我谦虚的意见中,不要将所有内容转换为C,因为您可能会获得几乎相同的性能,并尝试改进您的代码或使用opencv的其他功能来完成您想要的任务。
不信服?那么请编写一个简单的函数,一次使用C,一次使用C ++,并在100万次循环中运行它并自己测量时间。也许这可以帮助您做出正确的决定。

3
我从未在Android中使用过C或C++。但是在PC上,您可以使C++代码的运行速度与C代码一样快(有时甚至更快)。大部分C++是专门设计用于允许更多功能,但不以速度为代价(模板在编译时解决)。大多数编译器都很擅长优化您的代码,您的std :: vector调用将被内联,代码几乎与使用本机C数组相同。
我建议您寻找另一种提高性能的方法。也许在Android中有一些多媒体硬件扩展,您可以获得访问权并用于优化代码。

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接