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

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

本文关键字:C++ 元素 访问 numpy Python 风格 OpenCV      更新时间:2023-10-16

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

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

据我所知,这将更新A中的所有元素,其中由B中的行选择的列为"true"。

在OpenCV&C++

编辑#1

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

如果我理解你的问题,你指的是Opencv中Mat类型的矩阵?,如果是的话假设你有A是

Mat A(3,2,CV_8U);

你可以通过访问A

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.step1*i1+…+M.step[M.dims−1]*iM.dims-1

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

自行实现的解决方案

以下解决方案支持booolean indexing(沿维度通过布尔值进行选择)和list indexing(沿维度选择特定索引),适用于带有arbitrary number of dimensionscv::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 << "nn***************** test_boolean_indexing_2d() *****************nnn";
// 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 1n";
std::cout << "dst:n" << dst << "n";
}
void test_boolean_indexing_3d()
{
std::cout << "nn***************** test_boolean_indexing_3d() *****************nnn";
// 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 1n";
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 << "nn***************** test_list_indexing_2d() *****************nnn";
// 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 0n";
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]

Bug

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