2015-12-16 30 views
3

我想使用此公式Gray=B OR G OR R; pixel-wise operation將BGR cv :: Mat轉換爲灰色。我試過這個:優化Mat通道的獲取OR

cv::Mat diff_channels[3]; 
cv::split(diff, diff_channels); 
diff = diff_channels[0] | diff_channels[1] | diff_channels[2]; 

這可以通過更好的方法來實現嗎?

另外,如果我想實現Gray=MAX(B,G,R); pixel-wise operation的任何建議?

+0

'diff'的聲明是什麼? –

+0

對於最大值,請查看http://stackoverflow.com/a/24688707/2589776。 – herohuyongtao

+0

差異是另一個墊子 –

回答

5

OpenCV不包含任何合適的內置函數來以這種方式處理單獨的通道。如果你想獲得最高的性能,你可以自己實施這個程序。我建議你這樣的事情:

void calcOrChannels(const cv::Mat& src, cv::Mat& dst) 
{ 
    CV_Assert(src.type() == CV_8UC3); 
    int rows = src.rows, cols = src.cols; 

    dst.create(src.size(), CV_8UC1); 

    if (src.isContinuous() && dst.isContinuous()) 
    { 
    cols = rows * cols; 
    rows = 1; 
    } 

    for (int row = 0; row < rows; row++) 
    { 
    const uchar* src_ptr = src.ptr<uchar>(row); 
    uchar* dst_ptr = dst.ptr<uchar>(row); 

    for (int col = 0; col < cols; col++) 
    { 
     dst_ptr[col] = src_ptr[0] | src_ptr[1] | src_ptr[2]; // std::max(src_ptr[0], std::max(src_ptr[1], src_ptr[2])) 
     src_ptr += 3; 
    } 
    } 
} 

請注意,您需要在您的硬件測試此功能的性能,因爲它使用SIMD指令並行這OpenCV的實現(或者也許以後Implemeted一個)失去的利益。 但是這個過程使用更少的額外內存和算術操作。我想在大多數系統上(特別是嵌入式)它會工作得更快。它也取決於矩陣的大小。

我的計時系統(核心i7-4790)上:

| Matrix size | OpenCV (ms) | My (ms) | 
|:-----------:|------------:|---------| 
| 1280*720 | 4   | 1  | 
| 1920*1080 | 8   | 2  | 
| 4096*3112 | 41   | 17  | 
+0

謝謝,但在這種情況下,我不會像優化並行過程一樣優化OpenCV嗎? –

+1

@HumamHelfawi好問題!我更新了答案。你也可以使用來自OpenCV的並行後端('cv :: parallel_for')加速這個函數。如果您遇到問題,請立即告訴我,我會幫助您。 – akarsakov

+0

非常感謝您的基準測試 –

5

您可以使用cv::ParallelLoopBodycv::parallel_for_使用OpenCV的併發API:

class ParallelBGRtoGrayOR : public ParallelLoopBody 
{ 
    const Mat3b src; 
    mutable Mat1b dst; 

public: 
    ParallelBGRtoGrayOR(const Mat3b& _src, Mat1b& _dst) : ParallelLoopBody(), src(_src), dst(_dst) {} 

    virtual void operator()(const Range& range) const 
    { 
     int rows = range.end - range.start; 
     int cols = src.cols; 
     int len = rows * cols; 

     const uchar* yS = src.ptr<uchar>(range.start); 
     uchar* yD = dst.ptr<uchar>(range.start); 

     for (int i = 0; i < len; ++i, yD++, yS += 3) 
     { 
      *yD = yS[0] | yS[1] | yS[2]; 
      //*yD = std::max(yS[0], std::max(yS[1], yS[2])); 
     } 
    } 
}; 

void cvtBgrToGray_OR_Miki(const Mat3b& src, Mat1b& dst) 
{ 
    dst.create(src.rows, src.cols); 
    parallel_for_(Range(0, src.rows), ParallelBGRtoGrayOR(src, dst), -1); 
} 

測試

與測試你和@akarsakov方法,我得到了(以毫秒爲單位):

Size:   akarsakov  Humam Helfawi Miki   OpenCV (not same operation) 

[10 x 10]  0.00109963  0.0711094  2.60722   0.0934685 
[100 x 100]  0.0106298  0.0373874  0.0461844  0.0395867 
[1000 x 1000] 1.1799   3.30622   0.747382  1.61646 
[1280 x 720] 1.07324   2.91585   0.520858  0.9893 
[1920 x 1080] 2.31252   6.87818   1.11502   1.94011 
[4096 x 3112] 14.3454   42.0125   6.79644   12.0754 
[10000 x 10000] 115.575   321.145   61.1544   93.8846 

考慮

@akarsakov方法(原始數據智能地工作)一般比較好的方法,因爲它的速度非常快,更容易編寫。使用ParallelLoopBody僅在大圖像時纔有優勢(至少在我的電腦上)。

我假定源圖像是連續的。這項檢查應該在實踐中完成。

#include <opencv2/opencv.hpp> 
#include <iostream> 
using namespace std; 
using namespace cv; 

class ParallelBGRtoGrayOR : public ParallelLoopBody 
{ 
    const Mat3b src; 
    mutable Mat1b dst; 

public: 
    ParallelBGRtoGrayOR(const Mat3b& _src, Mat1b& _dst) : ParallelLoopBody(), src(_src), dst(_dst) {} 

    virtual void operator()(const Range& range) const 
    { 
     int rows = range.end - range.start; 
     int cols = src.cols; 
     int len = rows * cols; 

     const uchar* yS = src.ptr<uchar>(range.start); 
     uchar* yD = dst.ptr<uchar>(range.start); 

     for (int i = 0; i < len; ++i, yD++, yS += 3) 
     { 
      *yD = yS[0] | yS[1] | yS[2]; 
      //*yD = std::max(yS[0], std::max(yS[1], yS[2])); 
     } 
    } 
}; 

void cvtBgrToGray_OR_Miki(const Mat3b& src, Mat1b& dst) 
{ 
    dst.create(src.rows, src.cols); 
    parallel_for_(Range(0, src.rows), ParallelBGRtoGrayOR(src, dst), -1); 
} 


// credits to @akarsakov 
void cvtBgrToGray_OR_akarsakov(const Mat3b& src, Mat1b& dst) 
{ 
    int rows = src.rows, cols = src.cols; 
    dst.create(src.size()); 
    if (src.isContinuous() && dst.isContinuous()) 
    { 
     cols = rows * cols; 
     rows = 1; 
    } 

    for (int row = 0; row < rows; row++) 
    { 
     const uchar* src_ptr = src.ptr<uchar>(row); 
     uchar* dst_ptr = dst.ptr<uchar>(row); 

     for (int col = 0; col < cols; col++) 
     { 
      dst_ptr[col] = src_ptr[0] | src_ptr[1] | src_ptr[2]; 
      //dst_ptr[col] = std::max(src_ptr[0], std::max(src_ptr[1], src_ptr[2])); 
      src_ptr += 3; 
     } 
    } 
} 

// credits to @Humam_Helfawi 
void cvtBgrToGray_OR_Humam_Helfawi(const Mat3b& src, Mat1b& dst) 
{ 
    cv::Mat channels[3]; 
    cv::split(src, channels); 
    dst = channels[0] | channels[1] | channels[2]; 
} 



int main() 
{ 
    vector<Size> sizes{ Size(10, 10), Size(100, 100), Size(1000, 1000), Size(1280, 720), Size(1920, 1080), Size(4096, 3112), Size(10000, 10000) }; 

    cout << "Size: \t\takarsakov \tHumam Helfawi \tMiki \tOpenCV (not same operation)" << endl; 

    for (int is = 0; is < sizes.size(); ++is) 
    { 
     Size sz = sizes[is]; 

     cout << sz << "\t"; 

     Mat3b img(sz); 
     randu(img, Scalar(0, 0, 0), Scalar(255, 255, 255)); 

     Mat1b gray_akarsakov; 
     Mat1b gray_Miki; 
     Mat1b gray_Humam; 
     Mat1b grayOpenCV; 


     double tic = double(getTickCount()); 

     cvtBgrToGray_OR_akarsakov(img, gray_akarsakov); 

     double toc = (double(getTickCount()) - tic) * 1000./getTickFrequency(); 
     cout << toc << " \t"; 



     tic = double(getTickCount()); 

     cvtBgrToGray_OR_Humam_Helfawi(img, gray_Humam); 

     toc = (double(getTickCount()) - tic) * 1000./getTickFrequency(); 
     cout << toc << " \t"; 



     tic = double(getTickCount()); 

     cvtBgrToGray_OR_Miki(img, gray_Miki); 

     toc = (double(getTickCount()) - tic) * 1000./getTickFrequency(); 
     cout << toc << " \t"; 


     tic = double(getTickCount()); 

     cvtColor(img, grayOpenCV, COLOR_BGR2GRAY); 

     toc = (double(getTickCount()) - tic) * 1000./getTickFrequency(); 
     cout << toc << endl; 

    } 


    getchar(); 


    return 0; 
} 
+0

謝謝!第一次看到我的名字在其他人的代碼:D 偉大的答案和努力,真的很感謝你 –

+0

很高興它幫助,我很好奇這個:D – Miki

+0

偉大的工作!謝謝你的努力!只有一點點評論:'int len = rows * cols;'只適用於連續的矩陣(不適用於子版本),但很少有這種情況。 – akarsakov

1

只是想分享我的成果

Size:   akarsakov  Humam Helfawi Miki OpenCV (not same operation) 
[10 x 10]  0.00733416  1.03216   1.15244   0.044005 
[100 x 100]  0.078231  0.0816536  0.185799  0.043516 
[1000 x 1000] 7.81039   5.89764   40.7481   3.49253 
[1280 x 720] 7.61432   5.31824   8.74916   1.70397 
[1920 x 1080] 16.0256   12.8186   9.32367   3.6045 
[4096 x 3112] 97.7365   72.6287   49.3452   22.9545 
[10000 x 10000] 763.509   575.718   402.729   197.01 

編輯:

測試代碼

您可以使用此代碼評估電腦的結果我有一個新的筆記本電腦,並再次在它和舊的測試代碼。看來結果的差異取決於OpenCV的

1.Intel(R)_Core(TM)_i5-3230M_CPU _ @ _ 2.60GHz

Size:   akarsakov  Humam Helfawi Miki OpenCV (not same operation) 
[10 x 10]  0.00276318  0.0627636  0.445661  0.0351318 
[100 x 100]  0.0303949  0.0734216  0.0457898  0.0663162 
[1000 x 1000] 3.01186   5.30727   2.11699   3.05805 
[1280 x 720] 2.59975   4.91806   1.82014   2.69528 
[1920 x 1080] 5.97478   11.5406   3.56213   5.52556 
[4096 x 3112] 37.3076   64.1728   22.4575   35.0398 
[10000 x 10000] 284.249   510.332   175.626   268.652 

2.Intel(R)_Core(TM的不同的內置配置)2_Duo_CPU _____ T6500 __ @ _ 2。10GHz

Size:   akarsakov  Humam Helfawi Miki OpenCV (not same operation) 
[10 x 10]  0.00586751  0.107571  24.1966   1.50844 
[100 x 100]  0.0704101  0.154511  0.308044  0.119306 
[1000 x 1000] 7.00825   11.072   3.44912   5.25778 
[1280 x 720] 6.63322   9.88529   3.91999   5.0177 
[1920 x 1080] 14.6199   21.8047   7.19357   10.9551 
[4096 x 3112] 85.8226   133.165   42.4392   64.2184 
[10000 x 10000] 675.604   1050.19   334.334   507.87