我正在处理视频中的帧并实时显示(实时)。算法很快,但我想知道是否有任何我可以做的优化会使它更加无缝。我不知道我的算法中的哪些函数占用了大部分时间,我的猜测是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];
}
}
}
谢谢!
更新 好吧,这样做的是循环遍历图像中的每个像素,并计算像素颜色和绿色之间的欧几里德距离。所以,总的来说这是一个绿屏算法。
我做了一些基准测试,不使用此算法的fps是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的表达式。
答案 0 :(得分:7)
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);
答案 1 :(得分:1)
过早优化总是一件坏事 - 如果真的有必要,需要才能得到证据支持。对于几乎所有情况,编译器都会很好地优化代码的细节 - 在高级函数中降低复杂性是你的工作。
不要试图优化这个特定的代码,而是首先检查你的性能是否在程序的其他地方没有瓶颈,然后检查是否可以避免在第一次调用该函数地点。只有当您确定除了优化此代码之外没有什么可做的事情,您应该开始考虑优化此代码。
如果你真的真的必须优化这段代码,那么最好的方法就是使用MMX和SIMD指令将所有双“三元组”基本上矢量化为单个指令。
答案 2 :(得分:1)
嗯,不知道你的算法做了什么,如果你想稍微改进它,你可以摆脱sqrt
调用。只需替换:
double euc = sqrt(sum);
if (euc < threshold) {
....
}
人:
if (sum < threshold_2) {
....
}
threshold_2
等于threshold * threshold
,您可以预先计算并取出循环。
这会给它带来一点性能提升,但不要期望太多。
答案 3 :(得分:0)
sqrt
非常慢。为什么不在外循环之前计算double threshold_sq = threshold * threshold;
并使用sum < threshold_sq
进行比较。此外,restrict
关键字可能会或可能不会对您有所帮助。
答案 4 :(得分:0)
我建议调查像Valgrind这样的东西。它有许多有用的测试,几乎可以分析你的每一段代码。
答案 5 :(得分:0)
鉴于你的评论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
存储为整数):
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;
答案 6 :(得分:0)
如果您在Linux上,请查看oprofile和实用程序perf(随内核源代码提供)。
顺便说一下,UPDATE2中的代码可能根本不做任何事情,它被编译出来,因为任务的效果都没有存储在任何地方。在这种情况下,编译器决定不将它放在输出中。使用-S(汇编程序输出)编译代码并查看。答案 7 :(得分:-1)
您正在使用嵌套的for loops
,但我没有看到您使用外部循环中的变量。如果写的内容实际上是正确的,我建议您修改外部for loop
,这会将您的运行时间从O(n^2)
更改为O(n)
。