我怎样才能更快?(C/C++)OpenCV
How can I make this faster? (C/C++) OpenCV
我正在处理视频中的帧并实时显示。算法很快,但我想知道我是否可以做任何优化,使其更加无缝。我不知道我的算法中哪些函数占用的时间最多,我猜是sqrt()函数,因为它显然会进行一些查找,但我不确定。
这是我的算法:
IplImage *videoFrame = cvCreateImage(cvSize(bufferWidth, bufferHeight), IPL_DEPTH_8U, 4);
videoFrame->imageData = (char*)bufferBaseAddress;
int channels = videoFrame->nChannels;
int widthStep = videoFrame->widthStep;
int width = videoFrame->width;
int height = videoFrame->height;
for(int i=0;i<height;i++){
uchar *col = ((uchar *)(videoFrame->imageData + i*widthStep));
for(int j=0;j<width;j++){
double pRed = col[j*channels + 0];
double pGreen = col[j*channels + 1];
double pBlue = col[j*channels + 2];
double dRed = green.val[0] - pRed;
double dGreen = green.val[1] - pGreen;
double dBlue = green.val[2] - pBlue;
double sDRed = dRed * dRed;
double sDGreen = dGreen * dGreen;
double sDBlue = dBlue * dBlue;
double sum = sDRed + sDGreen + sDBlue;
double euc = sqrt(sum);
//NSLog(@"%f %f %f", pRed, pGreen, pBlue);
if (euc < threshold) {
col[j*channels + 0] = white.val[0];
col[j*channels + 1] = white.val[1];
col[j*channels + 2] = white.val[2];
}
}
}
谢谢!
更新好的,这样做是在图像中的每个像素上循环,并计算像素颜色和绿色之间的欧几里得距离。所以,总的来说,这是一个绿屏算法。
我做了一些基准测试,没有使用这个算法的帧速率是30.0fps。使用这个算法,它下降到大约8fps。但是,大部分for drop来自col[j*channels + 0];
。如果算法不做任何其他事情,而是使用数组选择的访问,则它会下降到大约10fps。
更新2好吧,这很有趣,我从双循环内的东西中删除了随机行,看看是什么导致了更大的开销,这就是我发现的:在堆栈上创建变量会导致FPS的巨大下降。考虑这个例子:
for(int i=0;i<height;i++){
uchar *col = ((uchar *)(data + i*widthStep));
for(int j=0;j<width;j++){
double pRed = col[j*channels + 0];
double pGreen = col[j*channels + 1];
double pBlue = col[j*channels + 2];
}
}
这会将fps降低到11 ish。
另一方面:
for(int i=0;i<height;i++){
uchar *col = ((uchar *)(data + i*widthStep));
for(int j=0;j<width;j++){
col[j*channels + 0];
col[j*channels + 1];
col[j*channels + 2];
}
}
一点也不降低FPS!FPS保持在30.0。我想我应该更新一下,让你们知道这是真正的瓶颈,让变量不堆积。我想知道我是否内联了所有我可能得到的纯30.0fps。
Nvm。。。也许那些没有赋值给var的表达式甚至都没有求值。
sqrt
是一个单调递增函数,您似乎只在阈值测试中使用它。
由于单调性,sqrt(sum) < threshold
等价于sum < threshold * threshold
(假设阈值为正)。
没有更昂贵的平方根,编译器将把乘法移到循环之外。
作为下一步,您可以从内部循环中删除昂贵的乘法j * channels
。编译器应该足够聪明,只做一次,并使用结果三次,但它仍然是一个乘法,其余的计算都依赖于它,因此会影响流水线操作。
还记得乘法和重复加法是一样的吗?通常情况下,做更多的操作会更昂贵,但在这种情况下,由于循环,您已经有了重复部分。所以使用:
for(int j=0;j<width;j++){
double pRed = col[0];
double pGreen = col[1];
double pBlue = col[2];
double dRed = green.val[0] - pRed;
double dGreen = green.val[1] - pGreen;
double dBlue = green.val[2] - pBlue;
double sDRed = dRed * dRed;
double sDGreen = dGreen * dGreen;
double sDBlue = dBlue * dBlue;
double sum = sDRed + sDGreen + sDBlue;
//NSLog(@"%f %f %f", pRed, pGreen, pBlue);
if (sum < threshold * threshold) {
col[0] = white.val[0];
col[1] = white.val[1];
col[2] = white.val[2];
}
col += channels;
}
接下来,您将在uchar
和double
之间进行昂贵的转换。阈值测试不需要这些:
int j = width;
do {
int_fast16_t const pRed = col[0];
int_fast16_t const pGreen = col[1];
int_fast16_t const pBlue = col[2];
int_fast32_t const dRed = green.val[0] - pRed;
int_fast32_t const dGreen = green.val[1] - pGreen;
int_fast32_t const dBlue = green.val[2] - pBlue;
int_fast32_t const sDRed = dRed * dRed;
int_fast32_t const sDGreen = dGreen * dGreen;
int_fast32_t const sDBlue = dBlue * dBlue;
int_fast32_t const sum = sDRed + sDGreen + sDBlue;
//NSLog(@"%f %f %f", pRed, pGreen, pBlue);
if (sum < threshold * threshold) {
col[0] = white.val[0];
col[1] = white.val[1];
col[2] = white.val[2];
}
col += channels;
} while (--j);
过早优化总是一件坏事,如果真的有必要,需要有确凿证据支持。在几乎所有情况下,编译器都会很好地优化代码的细节——你的工作是降低高级函数的复杂性。
与其试图优化这段特定的代码,不如先检查你的性能是否在程序的其他地方受到瓶颈影响,然后检查你是否可以从一开始就避免调用这个函数。只有当你确定除了优化这个代码之外别无选择时,你才应该开始考虑优化这个代码。
如果您真的真的必须优化此代码,最好的方法是使用MMX和SIMD指令将所有双"三元组"矢量化为单个指令。
好吧,在不知道算法的作用的情况下,如果你想稍微改进一下,你可以取消sqrt
调用。只需更换:
double euc = sqrt(sum);
if (euc < threshold) {
....
}
签字人:
if (sum < threshold_2) {
....
}
其中threshold_2
等于threshold * threshold
,您可以预先计算并从循环中取出。
这会给它带来一点性能提升,但不要期望太多。
sqrt
相当慢。为什么不在外循环之前计算double threshold_sq = threshold * threshold;
,并使用sum < threshold_sq
进行比较。此外,restrict
关键字可能对您有帮助,也可能没有帮助。
我建议研究类似Valgrind的东西。它有一堆有用的测试,可以分析几乎每一段代码。
鉴于您对col[j*channels + 0];
花费大量时间的评论:channels
总是3吗?甚至总是4?如果是这样的话,你可以通过推进指针来避免偏移数学,就像这样:
for(int i=0;i<height;i++){
uchar *col = ((uchar *)(videoFrame->imageData + i*widthStep));
for(int j=0;j<width;j++){
double dRed = green.val[0] - *col++;
double dGreen = green.val[1] - *col++;
double dBlue = green.val[2] - *col++;
//math here
if (euc < thresholdSqrd) {
*(col-3) = white.val[0];
*(col-2) = white.val[1];
*(col-1) = white.val[2];
}
col++; //do this only if `channels`==4
}
此外,由于您的原始数据似乎是rgb作为连续字节,您可以使用*(int32_t*)(col-3) |= 0xFFFFFF;
将像素设置为白色
将减法作为整数可能会稍微快一点(将green
存储为int):
int16_t iRed = green.val[0] - *col++;
int16_t iGreen = green.val[1] - *col++;
int16_t iBlue = green.val[2] - *col++;
double euc = (double)iRed*iRed + iGreen*iGreen + iBlue*iBlue;
如果您在Linux上,请查看opfile和实用程序perf(随内核源代码提供)。
顺便说一句,UPDATE2中的代码可能根本没有任何作用,它是编译出来的,因为驱逐的效果没有存储在任何地方。在这种情况下,编译器决定根本不把它放在输出中。使用-S(汇编程序输出)编译代码并查看。
您正在使用嵌套的for loops
,但我认为您根本没有使用外部循环中的变量。如果所写的内容实际上是正确的,我建议您修改外部for loop
,这将使您的运行时间从O(n^2)
更改为O(n)
。
- C++,OpenCV,尝试显示图像时"OpenCV(4.3.0) Error: Assertion failed (size.width>0 && size.height>0)"此错误
- 尝试导入pybind-opencv模块时出现libgtk错误
- 在编译C++代码(具有dlib和opencv)到WASM时面临问题
- 如何使用OpenCV将RBG图像转换为HSV,并将H、S和V值保存为C++中的3个独立图像
- OpenCV EqualizeHist()从彩色图像创建黑白图像
- 将OpenCV C++重写为EmguCV C#-如何使用指针
- OpenCV C++.快速计算混淆矩阵
- 在C++代码中包含opencv时,使用ctypes创建.so文件
- 哪些库可以通过Opencv调整曝光率
- 安装opencv失败-粘贴CMakeError.log的内容
- C++中的openCV Mat访问冲突
- OpenCV Android C++ imwrite not found
- 未定义的引用 .. 使用 OpenCV 编译 C++ 代码时,从命令行
- 将 OpenCV 与 CMAKE 中的项目一起构建为第三方库的正确方法
- CV_OCL_RUN宏如何在OpenCV(版本3.4.5)的goodFeaturesToTrack实现中工作?
- OpenCV 4.1.2 - 从网络摄像头获取帧并将其拆分
- C++ OpenCV 卡尔曼滤波器构造函数错误
- 使用OpenCV和覆盆子上的多个网络摄像头拍摄延时摄影,出现多个V4L错误
- 如何使用OpenCV-C++编写*.mp4视频?
- Mingw-64 在构建和安装后不会编译 openCV 代码