Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to indexing a matrix in opencv

Tags:

c++

opencv

matlab

Let say, A and B are matrices of the same size. In Matlab, I could use simple indexing as below.

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

How can I do this in OpenCV? Should I just use

for (i=0; ... rows)
    for(j=0; ... cols)
        if (A.at<double>(i,j)>0) B.at<double>(i,j) = 0;

something like this? Is there a better (faster and more efficient) way?

Moreover, in OpenCV, when I try

Mat idx = A>0;

the variable idx seems to be a CV_8U matrix (not boolean but integer).

like image 811
YW P Kwon Avatar asked Nov 17 '15 06:11

YW P Kwon


2 Answers

You can easily convert this MATLAB code:

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

// same as 

B(A>0) = 0;

to OpenCV as:

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

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

// or

B.setTo(0, A > 0);

Regarding performance, in C++ it's usually faster (it depends on the enabled optimizations) to work on raw pointers (but is less readable):

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

Also note that in OpenCV there isn't any boolean matrix, but it's a CV_8UC1 matrix (aka a single channel matrix of unsigned char), where 0 means false, and any value >0 is true (typically 255).

Evaluation

Note that this may vary according to optimization enabled with OpenCV. You can test the code below on your PC to get accurate results.

Time in ms:

          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

Note: I'm not entirely sure about the performance drop with OpenCV 3.0, so I just remark: test the code below on your PC to get accurate results.

As @AdrienDescamps stated in comments:

It seems that the performance drop with OpenCV 3.0 is related to the OpenCL option, that is now enabled in the comparison operator.

C++ Code

#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 Code

% 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

UPDATE

OpenCV 3.0 uses IPP optimization in the function setTo. If you have that enabled (you can check with cv::getBuildInformation()), you'll have a faster computation.

like image 86
Miki Avatar answered Sep 29 '22 00:09

Miki


The answer of Miki is very good, but i just want to add some clarification about the performance problem to avoid any confusion.

It is true that the best way to implement an image filter (or any algorithm) with OpenCV is to use the raw pointers, as shown in the second C++ example of Miki (C++ Loop). Using the at function is also correct, but significantly slower.

However, most of the time, you don't need to worry about that, and you can simply use the high level functions of OpenCV (first example of Miki , C++ Mask). They are well optimized, and will usually be almost as fast as a low level loop on pointers, or even faster.

Of course, there are exceptions (we just found one), and you should always test for your specific problem.

Now, regarding this specific problem :

The example here where the high level function was much slower (100x slower) than the low level loop is NOT a normal case, as it is demonstrated by the timings with other version/configuration of OpenCV, that are much lower. The problem seems to be that when OpenCV3.0 is compiled with OpenCL, there is a huge overhead the first time a function that uses OpenCL is called. The simplest solution is to disable OpenCL at compile time, if you use OpenCV3.0 (see also here for other possible solutions if you are interested).

like image 41
Adrien Descamps Avatar answered Sep 28 '22 23:09

Adrien Descamps