Search code examples
c++opencvmat

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.


Solution

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