我在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、注意抓图
在移动设备上它可能会非常慢。优化它是特定于设备和操作系统的。