我怎样才能让它更快? (C / C ++)OpenCV

时间:2012-03-05 23:02:00

标签: c++ c algorithm image-processing 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];
        }

    }
}

谢谢!

更新 好吧,这样做的是循环遍历图像中的每个像素,并计算像素颜色和绿色之间的欧几里德距离。所以,总的来说这是一个绿屏算法。

我做了一些基准测试,不使用此算法的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的表达式。

8 个答案:

答案 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;
}

接下来,您在uchardouble之间进行了昂贵的转换。阈值测试不需要这些:

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)