简体   繁体   中英

Accessing matrix elements in OpenCV & C++ in the style of numpy/Python

Say I have two matrices, A and B. If B is a matrix of boolean values, then in numpy/Python, I can write

A[:, B[:, i]] += 1

which, to my knowledge, would update all the elements in A with columns selected by a row in B being "true".

What would be the most efficient equivalent in OpenCV & C++?

Edit #1

I know how to access elements using .at() , but I'm looking for alternatives, that are hopefully more succinct too!

If I understand your question, you mean Matrix of type Mat in Opencv?, if so suppost you have A is

Mat A(3,2,CV_8U);

you can access to A by

A.at<double>(0,0),A.at<double>(0,1),A.at<double>(0,2);  // first row
A.at<double>(1,0),A.at<double>(1,1),A.at<double>(1,2); // second row

So I decided to go with using the Eigen library, without having another solution, based on the example below - what I was/am trying to do is apparently known as logical indexing .

My current solution: Eigen boolean array slicing

background

I think the reason opencv does not supply a functionality like that is the way cv::Mat is organized. The memory address of the beginning of each dimension is equidistantly spaced (see cv::Mat.step attribute).

To cite from the docs cv::Mat Class Reference

The class Mat represents an n-dimensional dense numerical single-channel or multi-channel array. It can be used to store real or complex-valued vectors and matrices, grayscale or color images, voxel volumes, vector fields, point clouds, tensors, histograms (though, very high-dimensional histograms may be better stored in a SparseMat ). The data layout of the array M is defined by the array M.step[], so that the address of element (i0,...,iM.dims−1), where 0≤ik<M.size[k], is computed as:

addr(Mi0,...,iM.dims−1)=M.data+M.step[0]∗i0+M.step 1 ∗i1+...+M.step[M.dims−1]∗iM.dims−1

A cv::Mat returned from a boolean indexing operation, could not be represented in this layout anymore - hence copying is necessary.

self implemented solution

The following solution supports booolean indexing (select by boolean values along a dimension) and list indexing (select specific indices along a dimension) and works for cv::Mat with an arbitrary number of dimensions . It is not as flexible as numpy , as it only works along one axis/dimension .

code

#include <iostream>
#include <opencv2/core/core.hpp>

/**
 * Reduce cv::Mat to certain elements in one dimension, which listed by index in listInds.
 *
 * @param[in] src source mat
 * @param[in] dim dimension index, along which to apply boolInds
 * @param[in] listInds index of the elements to be selected
 * @returns mat reduced to selected elements along dimension dim
 */
cv::Mat mat_list_indexing(cv::Mat &src, const int dim, const std::vector<int> &listInds)
{
    int *ns = new int[src.dims];  // ns: new size
    std::vector<int> size;
    for (int ii=0; ii< src.dims; ++ii)
    {
        ns[ii] = src.size[ii];
    }
    ns[dim] = listInds.size();
    
    cv::Mat dst(src.dims, ns, src.type());

    // loop over all indices of dst
    int dd;
    std::vector<int> index (src.dims, 0);
    while (true)
    {
        int srcOffset = 0;
        int dstOffset = 0;
        for (int ii=0; ii<src.dims; ++ii)
        {
            dstOffset += dst.step[ii] * index[ii];
            if (ii != dim)
            {
                srcOffset += src.step[ii] * index[ii];
            }
            else
            {
                srcOffset += src.step[ii] * listInds[index[ii]];
            }
        }
        memcpy(dst.data + dstOffset, src.data + srcOffset, src.elemSize());

        // update index
        dd = src.dims - 1;
        while (index[dd] == ns[dd] - 1)
        {
            --dd;
            if (dd < 0)
            {
                // break;
                delete [] ns;
                return dst;
            }
        }
        index[dd] += 1;
        for (int ii=dd+1; ii<src.dims; ++ii)
        {
            index[ii] = 0;
        }
    }
}

/**
 * Reduce cv::Mat to certain elements in one dimension, which are marked by a boolean single row cv::Mat.
 * https://stackoverflow.com/questions/21749348/accessing-matrix-elements-in-opencv-c-in-the-style-of-numpy-python
 *
 * @param[in] src source mat
 * @param[in] dim dimension index, along which to apply boolInds
 * @param[in] boolInds boolean indices to select elements along dimension dim; single row or single col mat
 * @returns mat reduced to selected elements along dimension dim
 */
cv::Mat mat_boolean_indexing(cv::Mat &src, int dim, cv::Mat1b boolInds)
{
    boolInds = boolInds.reshape(0, 1);

    std::vector<int> listInds;
    for (size_t ii=0; ii < boolInds.cols; ++ii)
    {
        if (boolInds(0, ii) > 0)
        {
            listInds.push_back(ii);
        }
    }


    return mat_list_indexing(src, dim, listInds);
}



void test_boolean_indexing_2d()
{
    std::cout << "\n\n***************** test_boolean_indexing_2d() *****************\n\n\n";

    // init
    cv::Mat1f src = (cv::Mat1f(3, 4) <<
        0,      1,      2.2,    NAN,                
        -.4,    .5,     .6,     7,                
        -70,    NAN,    8.8,    9                 
        );                                          
    cv::Mat1b boolInds {true, false, true, true};
    boolInds = boolInds.reshape(0, 1);

    // test indexing
    cv::Mat1f dst = mat_boolean_indexing(src, 1, boolInds);

    // cout
    std::cout << "src:\n" << src << "\n";
    std::cout << "boolInds: " << boolInds << " along dim 1\n";
    std::cout << "dst:\n" << dst << "\n";

}

void test_boolean_indexing_3d()
{
    std::cout << "\n\n***************** test_boolean_indexing_3d() *****************\n\n\n";

    // init
    const int sz[] = {2, 4, 3};
    cv::Mat1f src(3, sz);
    for (int i0=0; i0<sz[0]; ++i0)
    {
        for (int i1=0; i1<sz[1]; ++i1)
        {
            for (int i2=0; i2<sz[2]; ++i2)
            {
                src.at<float>(i0, i1, i2) = 100*i0 + 10*i1 + i2;
            }
        }
    }
    cv::Mat1b boolInds {true, false, false, true};
    boolInds = boolInds.reshape(0, 1);

    // test indexing
    cv::Mat1f dst = mat_boolean_indexing(src, 1, boolInds);

    // cout
    std::cout << "\nsrc:\n";
    for (int i0=0; i0<sz[0]; ++i0)
    {
        for (int i1=0; i1<sz[1]; ++i1)
        {
            for (int i2=0; i2<sz[2]; ++i2)
            {
                std::cout << "src(" << i0 <<", " << i1 << ", " << i2 << ")=" << src(i0, i1, i2) << "\n";
            }
        }
    }
    std::cout << "boolInds: " << boolInds << " along dim 1\n";
    std::cout << "dst:\n";
    for (int i0=0; i0<dst.size[0]; ++i0)
    {
        for (int i1=0; i1<dst.size[1]; ++i1)
        {
            for (int i2=0; i2<dst.size[2]; ++i2)
            {
                std::cout << "dst(" << i0 <<", " << i1 << ", " << i2 << ")=" << dst(i0, i1, i2) << "\n";
            }
        }
    }
}

void test_list_indexing_2d()
{
    std::cout << "\n\n***************** test_list_indexing_2d() *****************\n\n\n";

    // init
    cv::Mat1f src = (cv::Mat1f(4, 2) <<
        0,      1,
        -.4,    .5,
        -70,    NAN,
        10, 100
        );                                          
    std::vector<int> listInds {3, 2};

    // test indexing
    cv::Mat1f dst = mat_list_indexing(src, 0, listInds);

    // cout
    std::cout << "src:\n" << src << "\n";
    std::cout << "listInds: {";
    for (size_t ii=0; ii<listInds.size(); ++ii)
    {
        std::cout << listInds[ii] << "; ";
    }
    std::cout << "} along dim 0\n";
    std::cout << "dst:\n" << dst << "\n";

}

int main()
{
    test_boolean_indexing_2d();
    test_boolean_indexing_3d();
    test_list_indexing_2d();
}

output

***************** test_boolean_indexing_2d() *****************


src:
[0, 1, 2.2, nan;
 -0.40000001, 0.5, 0.60000002, 7;
 -70, nan, 8.8000002, 9]
boolInds: [  1,   0,   1,   1] along dim 1
dst:
[0, 2.2, nan;
 -0.40000001, 0.60000002, 7;
 -70, 8.8000002, 9]


***************** test_boolean_indexing_3d() *****************



src:
src(0, 0, 0)=0
src(0, 0, 1)=1
src(0, 0, 2)=2
src(0, 1, 0)=10
src(0, 1, 1)=11
src(0, 1, 2)=12
src(0, 2, 0)=20
src(0, 2, 1)=21
src(0, 2, 2)=22
src(0, 3, 0)=30
src(0, 3, 1)=31
src(0, 3, 2)=32
src(1, 0, 0)=100
src(1, 0, 1)=101
src(1, 0, 2)=102
src(1, 1, 0)=110
src(1, 1, 1)=111
src(1, 1, 2)=112
src(1, 2, 0)=120
src(1, 2, 1)=121
src(1, 2, 2)=122
src(1, 3, 0)=130
src(1, 3, 1)=131
src(1, 3, 2)=132
boolInds: [  1,   0,   0,   1] along dim 1
dst:
dst(0, 0, 0)=0
dst(0, 0, 1)=1
dst(0, 0, 2)=2
dst(0, 1, 0)=30
dst(0, 1, 1)=31
dst(0, 1, 2)=32
dst(1, 0, 0)=100
dst(1, 0, 1)=101
dst(1, 0, 2)=102
dst(1, 1, 0)=130
dst(1, 1, 1)=131
dst(1, 1, 2)=132


***************** test_list_indexing_2d() *****************


src:
[0, 1;
 -0.40000001, 0.5;
 -70, nan;
 10, 100]
listInds: {3; 2; } along dim 0
dst:
[10, 100;
 -70, nan]

bugs

I just developed this and it is only tested on these 3 examples. Some things are not yet tested, for instance multi channel cv::Mats . I will keep this answer updated and document changes in this section. Please report bugs in the comments.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM