在opencv中索引矩阵的最佳方法

时间:2015-11-17 06:52:13

标签: c++ matlab opencv

假设AB是相同大小的矩阵。 在Matlab中,我可以使用如下的简单索引。

idx = A>0;
B(idx) = 0

我如何在OpenCV中执行此操作?我应该只使用

for (i=0; ... rows)
    for(j=0; ... cols)
        if (A.at<double>(i,j)>0) B.at<double>(i,j) = 0;
这样的事情?是否有更好(更快,更有效)的方式?

此外,在OpenCV中,当我尝试

Mat idx = A>0;

变量idx似乎是CV_8U矩阵(不是布尔值而是整数)。

2 个答案:

答案 0 :(得分:6)

您可以轻松转换此MATLAB代码:

idx = A > 0;
B(idx) = 0;

// same as 

B(A>0) = 0;

以OpenCV格式:

Mat1d A(...)
Mat1d B(...)

Mat1b idx = A > 0;
B.setTo(0, idx) = 0;

// or

B.setTo(0, A > 0);

关于性能,在C ++中,它通常更快(取决于启用的优化)来处理原始指针(但是可读):

for (int r = 0; r < B.rows; ++r)
{
    double* pA = A.ptr<double>(r);
    double* pB = B.ptr<double>(r);
    for (int c = 0; c < B.cols; ++c)
    {
        if (pA[c] > 0.0) pB[c] = 0.0;
    }
}

另请注意,在OpenCV中没有任何布尔矩阵,但它是CV_8UC1矩阵(也就是unsigned char的单个通道矩阵) ,其中0表示false,任何值>0都为真(通常为255)。

<强>评价

请注意,根据OpenCV启用的优化,可能会有所不同。您可以在PC上测试以下代码,以获得准确的结果。

以毫秒为单位的时间:

          my results           my results      @AdrienDescamps
          (OpenCV 3.0 No IPP)  (OpenCV 2.4.9)

Matlab  : 13.473     
C++ Mask: 640.824              5.81815         ~5
C++ Loop: 5.24414              4.95127         ~4

注意:我对OpenCV 3.0的性能下降并不完全确定,所以我只想说:在PC上测试下面的代码以获得准确的结果。

@AdrienDescamps在评论中说:

  

OpenCV 3.0的性能下降似乎与OpenCL选项有关,现在在比较运算符中启用了该选项。

C ++代码

#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
    // Random initialize A with values in [-100, 100]
    Mat1d A(1000, 1000);
    randu(A, Scalar(-100), Scalar(100));

    // B initialized with some constant (5) value
    Mat1d B(A.rows, A.cols, 5.0); 

    // Operation: B(A>0) = 0;

    {
        // Using mask

        double tic = double(getTickCount());
        B.setTo(0, A > 0);
        double toc = (double(getTickCount()) - tic) * 1000 / getTickFrequency();

        cout << "Mask: " << toc << endl;
    }
    {
        // Using for loop

        double tic = double(getTickCount());
        for (int r = 0; r < B.rows; ++r)
        {
            double* pA = A.ptr<double>(r);
            double* pB = B.ptr<double>(r);
            for (int c = 0; c < B.cols; ++c)
            {
                if (pA[c] > 0.0) pB[c] = 0.0;
            }
        }
        double toc = (double(getTickCount()) - tic) * 1000 / getTickFrequency();

        cout << "Loop: " << toc << endl;
    }


    getchar();
    return 0;
}

Matlab代码

% Random initialize A with values in [-100, 100]
A = (rand(1000) * 200) - 100;
% B initialized with some constant (5) value
B = ones(1000) * 5; 

tic
B(A>0) = 0; 
toc

<强>更新

OpenCV 3.0在函数setTo中使用IPP优化。如果您启用了该功能(可以使用cv::getBuildInformation()查看),那么您的计算速度会更快。

答案 1 :(得分:4)

Miki的答案非常好,但我只想补充一些关于性能问题的澄清,以避免混淆。

使用OpenCV实现图像过滤器(或任何算法)的最佳方法是使用原始指针,如Miki(C ++ Loop)的第二个C ++示例所示。 使用at函数也是正确的,但速度要慢得多。

但是,大多数情况下,你不必担心,你可以简单地使用OpenCV的高级功能(Miki,C ++ Mask的第一个例子)。它们经过了很好的优化,通常几乎和指针上的低级循环一样快,甚至更快。

当然,也有例外(我们刚刚找到一个),您应该始终测试您的具体问题。

现在,关于这个具体问题:

此处高级函数比低级循环慢得多(慢100倍)的示例不是正常情况,因为OpenCV的其他版本/配置的时序要低得多。 问题似乎是当使用OpenCL编译OpenCV3.0时,第一次调用使用OpenCL的函数时会产生巨大的开销。最简单的解决方案是在编译时禁用OpenCL,如果您使用OpenCV3.0(如果您有兴趣,另请参阅here了解其他可能的解决方案。)