17

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

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

完全重写我的应用程序是否有意义C?我知道使用诸如此类的东西std::vector对开发人员来说很舒服,但我不在乎。

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

我用谷歌搜索了这个问题,但没有找到任何东西。

感谢您的任何建议。

4

7 回答 7

56

我在Android和优化方面做了很多工作(我写了一个视频处理应用程序,可以在4ms内处理一帧),所以我希望能给你一些中肯的答案。

OpenCV 中的 C 和 C++ 接口没有太大区别。一些代码是用 C 编写的,并且有一个 C++ 包装器,而一些反之亦然。两者之间的任何显着差异(由 Shervin Emami 衡量)要么是回归,要么是错误修复,要么是质量改进。您应该坚持使用最新的 OpenCV 版本。

为什么不重写?

您将花费大量时间,您可以更好地利用这些时间。C 接口很繁琐,引入错误或内存泄漏的机会很高。在我看来,你应该避免它。

优化建议

A.打开优化。

编译器优化和调试断言的缺乏都会对您的运行时间产生很大影响。

B.分析您的应用程序。

首先在您的计算机上执行此操作,因为它更容易。使用 Visual Studio Profiler,识别慢速部分。优化它们。永远不要因为你认为慢而优化,而是因为你衡量它。从最慢的功能开始,尽可能优化它,然后再慢一点。衡量您的更改以确保它确实更快。

C.专注于算法。

更快的算法可以将性能提高几个数量级(100 倍)。C++ 技巧可能会给您带来 2 倍的性能提升。

经典技法:

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

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

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

D.在重要的地方使用 C

在循环中,使用 C 风格而不是 C++ 可能是有意义的。指向数据矩阵或浮点数组的指针比 mat.at 或 std::vector<> 快得多。通常瓶颈是嵌套循环。专注于它。到处替换 vector<> 并将您的代码意大利化是没有意义的。

E.避免隐藏成本

一些 OpenCV 函数将数据转换为双精度,对其进行处理,然后再转换回输入格式。当心它们,它们会破坏移动设备的性能。示例:翘曲、缩放、类型转换。此外,众所周知,色彩空间转换是惰性的。更喜欢直接从原生 YUV 获得的灰度。

F.使用矢量化

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 intrinsics得到了很好的结果,就像上面的例子一样。

另一种快速启动的方法是将 OpenCV 中手动编码的 SSE 优化代码转换为 NEON。SSE 是英特尔处理器中的 NEON 等价物,许多 OpenCV 函数都使用它,就像这里一样。这是 uchar 矩阵(常规图像格式)的图像过滤代码。不要一味地一一转换指令,而是以它为例。

您可以在此博客和以下帖子中阅读有关 NEON 的更多信息。

G、注意抓图

在移动设备上它可能会非常慢。优化它是特定于设备和操作系统的。

于 2012-07-07T16:46:10.573 回答
6

在做出这样的决定之前,您应该分析您的代码以定位代码中的热点。如果没有这些信息,您为加快速度所做的任何更改都将是猜测。你试过这个Android NDK 分析器吗?

于 2012-07-07T15:51:03.277 回答
4

shervin imami 在他的网站上有一些性能测试。你可以检查它以获得一些想法。

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

希望能帮助到你。

(而且,如果您有任何提高性能的方法,如果您在某处分享您自己的发现,那就太好了。)

于 2012-07-07T15:48:31.687 回答
3

我想这个问题需要表述为:C 比 C++ 快吗?答案是否定的。两者都被编译为本地机器语言,并且 C++ 被设计为与 C 一样快。至于 STL(尤其是 ISO 标准)也被设计并注意它们与指针一样快 + 它们提供了灵活性。使用 C 的唯一原因是您的平台不支持 C++ 在我谦虚的开放中,不要将所有内容都转换为 C,因为您可能会获得几乎相同的性能。而是尝试改进您的代码或使用 opencv 的其他功能来做您想做的事情。

不服气?好吧,然后编写一个简单的函数,一次在 C 中,一次在 C++ 中,并在 1 亿次循环中运行它并自己测量时间。也许这可以帮助您做出正确的决定

于 2012-07-07T15:53:51.003 回答
3

我从未在 Android 中使用过 C 或 C++。但是在 PC 中,您可以让 C++ 与 C 代码一样快(有时甚至更快)。大多数 C++ 都是专门为允许更多功能而设计的,但不以速度为代价(模板在编译时解决)。大多数编译器都非常擅长优化您的代码,并且您的 std::vector 调用将被内联,并且代码几乎与使用本机 C 数组相同。

我建议你寻找另一种方法来提高你的表现。也许在 Android 中有一些多媒体硬件扩展,您可以访问并使用它来优化代码。

于 2012-07-07T15:55:53.953 回答
3

我在多次测试中注意到:

  1. 直接访问像素而不是使用 Mat.at(x,y) 方法时,C 接口 (IplImage) 的速度要快很多倍,当我将 C++ 应用程序转换为 C 时,我的 blob 检测例程的性能提高了 3 倍

  2. 当从外部应用程序(例如 LabView)调用时,C++ 接口在某些例程中崩溃,而在 C 中调用相同例程时它可以工作。这方面的示例是 FindContours 和 cvFindContours

  3. C 与嵌入式设备的兼容性要好得多。但是,我还没有在这个领域做过任何事情。

于 2013-03-13T20:06:46.500 回答
1

我在 IOS 设备和讨论中遇到了类似的问题IOS/iPad/iPhone 的最大速度也包括一些适用于其他移动平台的提示。

于 2012-08-13T05:00:36.607 回答