繁体   English   中英

以numpy/Python的风格访问OpenCV&C++中的矩阵元素

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

假设我有两个矩阵,A 和 B。如果 B 是布尔值矩阵,那么在 numpy/Python 中,我可以写

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

据我所知,这将更新 A 中的所有元素,其中 B 中的一行选择的列为“真”。

OpenCV 和 C++ 中最有效的等价物是什么?

编辑 #1

我知道如何使用.at()访问元素,但我正在寻找替代方案,希望它们也更简洁!

如果我理解你的问题,你的意思是 Opencv 中的 Mat 类型的矩阵?,如果是这样,那么假设你有 A 是

Mat A(3,2,CV_8U);

你可以通过

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

所以我决定使用Eigen库,没有其他解决方案,基于下面的例子 - 我正在/正在尝试做的显然是逻辑索引

我目前的解决方案: 特征布尔数组切片

背景

我认为opencv不提供这样的功能的原因是cv::Mat的组织方式。 每个维度开头的内存地址等距间隔(参见cv::Mat.step属性)。

从文档cv::Mat Class Reference 中引用

类 Mat 表示一个 n 维密集数值单通道或多通道阵列。 它可用于存储实数或复值向量和矩阵、灰度或彩色图像、体素体积、向量场、点云、张量、直方图(不过,非常高维的直方图可能更好地存储在 SparseMat 中)。 数组M的数据布局由数组M.step[]定义,使得元素(i0,...,iM.dims−1)的地址,其中0≤ik<M.size[k],计算如下:

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

从布尔索引操作返回的cv::Mat无法再在此布局中表示 - 因此需要复制。

自我实现的解决方案

以下解决方案支持booolean indexing (沿维度通过布尔值选择)和list indexing (沿维度选择特定索引),并且适用于具有arbitrary number of dimensions cv::Mat 它不像numpy那样灵活,因为它只适用于one axis/dimension

代码

#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();
}

输出

***************** 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]

错误

我刚刚开发了这个,它只在这 3 个例子上进行了测试。 有些东西尚未测试,例如multi channel cv::Mats 我将更新此答案并记录本节中的更改。 请在评论中报告错误。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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