簡體   English   中英

我怎樣才能使它更快? (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];
        }

    }
}

謝謝!

更新好的,所以這樣做是循環遍歷圖像中的每個像素,並計算像素顏色和綠色之間的歐幾里得距離。 因此,總體而言,這是一個綠屏算法。

我做了一些基准測試,未使用此算法的fps為30.0fps。 使用此算法,它下降到大約8fps。 但是,大多數for drop都來自col[j*channels + 0]; 如果該算法沒有做任何其他事情,並且使用了數組選擇的訪問權限,則它會下降到大約10fps。

更新2好吧,這很有趣,我從double循環內的內容中刪除了隨機行,以查看導致更大開銷的原因,這就是我發現的:在堆棧上創建變量會導致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位。

現在,另一方面:

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 編譯器應該足夠聰明,只能執行一次並使用3次結果,但其余計算所依賴的結果仍然是乘數,因此會影響流水線化。

還記得乘法與重復加法相同嗎? 通常,執行更多操作會更昂貴,但是在這種情況下,由於循環的原因,您已經具有重復部分。 因此使用:

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);

過早的優化始終是一件壞事-如果確實有必要,則需要有確鑿的證據作為后盾。 在幾乎所有情況下,編譯器都能很好地優化代碼的細節-降低高級功能的復雜度是您的工作。

與其嘗試優化這段特定的代碼,不如先檢查您的性能在程序的其他地方是否處於瓶頸,然后檢查是否可以完全避免首先調用此函數。 只有在確定除了優化該代碼之外別無其他事情之后,您才應該開始考慮優化該代碼。

如果您真的真的必須優化此代碼,則最好的方法是使用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;將像素設置為白色*(int32_t*)(col-3) |= 0xFFFFFF;

並且將您的減為整數可能會稍快一些(將green存儲為ints):

      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,請查看oprofile和實用程序perf(隨內核源代碼提供)。

順便說一句,UPDATE2中的代碼可能根本不做任何事情,它被編譯出來了,因為經驗的影響沒有存儲在任何地方。 在這種情況下,編譯器決定根本不將其放入輸出中。 用-S(匯編器輸出)編譯代碼,然后看一看。

您正在使用嵌套的for loops但我完全看不到您使用外部循環中的變量。 如果所寫的內容實際上是正確的,我建議您修改外部for loop ,這會將運行時間從O(n^2)更改為O(n)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM