BossShell
BossShell

Reputation: 389

How to access matrix data in opencv by another mat with locations (indexing)

Suppose I have a Mat of indices (locations) called B, We can say that this Mat has dimensions of 1 x 100 and We suppose to have another Mat, called A, full of data of the same dimensions of B. Now, I would access to the data of A with B. Usually I would create a for loop and I would take for each elements of B, the right elements of A. For the most fussy of the site, this is the code that I would write:

for(int i=0; i < B.cols; i++){
    int index = B.at<int>(0, i);
    std::cout<<A.at<int>(0, index)<<std:endl;
}

Ok, now that I showed you what I could do, I ask you if there is a way to access the matrix A, always using the B indices, in a more intelligent and fast way. As someone could do in python thanks to the numpy.take() function.

Upvotes: 1

Views: 1795

Answers (2)

Rudy
Rudy

Reputation: 131

One fast way is to use pointer for both A (data) and B (indexes).

const int* pA = A.ptr<int>(0);
const int* pIndexB = B.ptr<int>(0);
int sum = 0;
for(int i = 0; i < Bi.cols; ++i)
{
    sum += pA[*pIndexB++];
}

Note: Be carefull with pixel type, in this case (as you write in your code) is int!

Note2: Using cout for each point access put the optimization useless!

Note3: In this article Satya compare four methods for pixel access and fastest seems "foreach": https://www.learnopencv.com/parallel-pixel-access-in-opencv-using-foreach/

Upvotes: 0

Nejc
Nejc

Reputation: 937

This operation is called remapping. In OpenCV, you can use function cv::remap for this purpose.

Below I present the very basic example of how remap algorithm works; please note that I don't handle border conditions in this example, but cv::remap does - it allows you to use mirroring, clamping, etc. to specify what happens if the indices exceed the dimensions of the image. I also don't show how interpolation is done; check the cv::remap documentation that I've linked to above.

If you are going to use remapping you will probably have to convert indices to floating point; you will also have to introduce another array of indices that should be trivial (all equal to 0) if your image is one-dimensional. If this starts to represent a problem because of performance, I'd suggest you implement the 1-D remap equivalent yourself. But benchmark first before optimizing, of course.

For all the details, check the documentation, which covers everything you need to know to use te algorithm.

cv::Mat<float> remap_example(cv::Mat<float> image, 
                             cv::Mat<float> positions_x, 
                             cv::Mat<float> positions_y)
{
   // sizes of positions arrays must be the same
   int size_x = positions_x.cols;
   int size_y = positions_x.rows;
   auto out = cv::Mat<float>(size_y, size_x);

   for(int y = 0; y < size_y; ++y)
     for(int x = 0; x < size_x; ++x)
     {
       float ps_x = positions_x(x, y);
       float ps_y = positions_y(x, y);

       // use interpolation to determine intensity at image(ps_x, ps_y),
       // at this point also handle border conditions 
       // float interpolated = bilinear_interpolation(image, ps_x, ps_y);

       out(x, y) = interpolated;
      }

return out;
}

Upvotes: 1

Related Questions