Search code examples
c++opencvmatrixsize-reduction

How to retrieve values in even/odd indices using OpenCV, c++ in an elegant way?


Consider, I have the following matrix

0   1  2  3  
4   5  6  7  
8   9 10 11  
12 13 14 15  

I want to retrieve the values in even indices (both x and y indices are even) without using for loop.

0  2
8 10

I have big sized images (many of 5000*5000+ grayscale matrices). Using for loop doesn't seem to be the best way. I'd like to hear if there is better way than for loops.

I tried using the following mask, then do the operations but it is not efficient because I need to do 4*n^2 multiplication rather than n^2(Assume original image is 2n*2n)

1 0 1 0
0 0 0 0
1 0 1 0
0 0 0 0

Note that, I do multiple operations on the matrix. Any help is appreciated.

Thanks in advance,


Solution

  • You can remove the useless rows and columns, and work on a matrix with half the size of the original matrix.

    You can do this easily with the resize function, with nearest interpolation:

        #include <opencv2/opencv.hpp>
    #include <iostream>
    using namespace cv;
    using namespace std;
    
    int main(int argc, char **argv)
    {
        Mat1b mat = (Mat1b(4,4) << 0, 1, 2, 3,
                                   4, 5, 6, 7,
                                   8, 9, 10, 11, 
                                   12, 13, 14, 15);
    
        Mat1b res;
        resize(mat, res, Size(0, 0), 0.5, 0.5, INTER_NEAREST);
    
        cout << "Mat:" << endl << mat << endl << endl;
        cout << "Res:" << endl << res << endl;
    
        return 0;
    }
    

    Then the values in res are only the values at the indices you need:

    Mat:
    [0, 1, 2, 3;
     4, 5, 6, 7;
     8, 9, 10, 11;
     12, 13, 14, 15]
    
    Res:
    [0, 2;
     8, 10]
    

    In order to restore the values to original position, you can use the Kronecker product (not available in OpenCV, but can be easily implemented) with a suitable pattern. This will produce:

    Mat:
    [0, 1, 2, 3;
     4, 5, 6, 7;
     8, 9, 10, 11;
     12, 13, 14, 15]
    
    Res:
    [0, 2;
     8, 10]
    
    Res Modified:
    [1, 3;
     9, 11]
    
    Restored:
    [1, 0, 3, 0;
     0, 0, 0, 0;
     9, 0, 11, 0;
     0, 0, 0, 0]
    

    Code:

    #include <opencv2/opencv.hpp>
    #include <algorithm>
    #include <iostream>
    using namespace cv;
    using namespace std;
    
    Mat kron(const Mat A, const Mat B)
    {
        CV_Assert(A.channels() == 1 && B.channels() == 1);
    
        Mat1d Ad, Bd;
        A.convertTo(Ad, CV_64F);
        B.convertTo(Bd, CV_64F);
    
        Mat1d Kd(Ad.rows * Bd.rows, Ad.cols * Bd.cols, 0.0);
    
        for (int ra = 0; ra < Ad.rows; ++ra)
        {
            for (int ca = 0; ca < Ad.cols; ++ca)
            {
                Kd(Range(ra*Bd.rows, (ra + 1)*Bd.rows), Range(ca*Bd.cols, (ca + 1)*Bd.cols)) = Bd.mul(Ad(ra, ca));
            }
        }
        Mat K;
        Kd.convertTo(K, A.type());
        return K;
    
    }
    
    
    int main(int argc, char **argv)
    {
        Mat1b mat = (Mat1b(4, 4) << 0, 1, 2, 3,
            4, 5, 6, 7,
            8, 9, 10, 11,
            12, 13, 14, 15);
    
        Mat1b res;
        resize(mat, res, Size(0, 0), 0.5, 0.5, INTER_NEAREST);
    
        cout << "Mat:" << endl << mat << endl << endl;
        cout << "Res:" << endl << res << endl << endl;
    
        // Work on Res
        res += 1;
    
        cout << "Res Modified:" << endl << res << endl << endl;
    
        // Define the pattern
        Mat1b pattern = (Mat1b(2,2) << 1, 0, 
                                       0, 0);
    
        // Apply Kronecker product
        Mat1b restored = kron(res, pattern);
    
        cout << "Restored:" << endl << restored << endl << endl;
    
        return 0;
    }