OpenCV 3D Mat 转换为向量。

3

我有一个3D矩阵,并希望将其转换为向量。我尝试使用opencv的reshape()函数,但似乎不能用于具有超过2维的矩阵。如何将3D矩阵转换为向量?我可以通过访问Mat中的所有元素来完成。是否有更有效的方法?


你能否发布通过访问所有元素来完成的代码?这会提示你想要拥有的向量的格式和顺序。 - Micka
3个回答

5
如果我们有以下的3D矩阵:
const int ROWS=2, COLS=3, PLANES=4;
int dims[3] = {ROWS, COLS, PLANES};
cv::Mat m = cv::Mat(3, dims, CV_32SC1);   // works with other types (e.g. float, double,...)

这仅适用于连续的mat对象(即m.isContinuous() == true)。

要获得包含相同元素的相同类型的向量:

  1. Using the overloaded STL vector constructor:

    Declaring and initializing a vector of same type as the matrix and copying all mat elements:

    const int* p3 = m3.ptr<int>(0);
    std::vector<int> flat0(p3, p3+m3.total());
    
  2. Using OpenCV's reshape:

    This differs from the first solution in how the elements are laid out in the end. Originally from this post: How to divide 3D matrix into bunches of 2D matrix with opencv C++ I second his suggestion on defining your dimensions as channels if possible. OpenCV handles channels better than dimensions. Of course, that might not be applicable if you intended to have more than 4 dimensions.

    cv::Mat m2(ROWS, COLS*PLANES, CV_32SC1, m3.data); // no copying happening here
    cv::Mat m2xPlanes = m2.reshape(PLANES); // not sure if this involves a copy
    std::vector<Mat> planes;
    cv::split(m2xPlanes, planes); // usually used for splitting multi-channel matrices
    
    std::vector<int> flat;
    for(size_t i=0; i<planes.size(); i++) {
        cv::Mat plane_i = planes[i];
        const int* plane_i_ptr = plane_i.ptr<int>(0);
        flat.insert(flat.end(), plane_i_ptr, plane_i_ptr+plane_i.total());
    }
    

使用这两种解决方案,所有元素都被考虑在内,只是它们的顺序不同,因此访问方式也不同。 在第一种解决方案中,您可以通过row、col和plane来访问元素。

int index = row * COLS * PLANES + col * PLANES + p

第二个元素按平面排序。

选择哪种解决方案可能取决于您将如何索引向量。


请注意,mat.total() = mat.rows * mat.cols。Total 意味着一个平面的总数。OpenCV 文档没有提到这一点,但您可以通过任何 RGB 图像进行检查。 - sziraqui

1
另一种变体,但根据实际问题,它从2D数组开始,然后使用Mat构造函数的3d-given-already-allocated-data形式来影响重塑,通过给出2d矩阵的3d切片视图来满足copyTo(因为源和参数将具有相同的尺寸3x4x1(如下所示的aslicesizes)。
int nRows = 3;
int nCols = 4;
int nPlanes = 2;
int aslicesizes[3] = {nRows,nCols,1};
int a3DMsizes[3] = {nRows,nCols,nPlanes};
Mat OneM = Mat::ones(nRows,nCols,CV_8UC1);  
Mat MAs3DPlane; // will be generic slice that will hava a 3d slice (plane) view of the 2d matrices (OneM, TwoM) before they are copied into OneTwo3D
//      Mat OneM = Mat::ones(3,aslicesizes,CV_8UC1);  
Mat TwoM = Mat::ones(nRows,nCols,CV_8UC1)+1;  
//      Mat TwoM = Mat::ones(3,aslicesizes,CV_8UC1)+1;  
Mat OneTwo3D = Mat::zeros(3,a3DMsizes,CV_8UC1); // target 3d array
Mat OneTwo3DPlaneM; // slice of the 3d target array
Range OneTwo3DRanges[] = {Range::all(),Range::all(),Range(0,1)}; // first slice range
OneTwo3DPlaneM = OneTwo3D(OneTwo3DRanges); // first target slice of OneTwo3D
MAs3DPlane = Mat::Mat(3,aslicesizes,CV_8UC1,OneM.data); // source slice of OneM.
MAs3DPlane.copyTo(OneTwo3DPlaneM); // copying OneM slice to first slice
OneTwo3DRanges[2] = Range(1,2); // reset ranges appropriate OneTwo3D's second slice (plane)
OneTwo3DPlaneM = OneTwo3D(OneTwo3DRanges); // set to second slice of OneTwo3d
MAs3DPlane = Mat::Mat(3,aslicesizes,CV_8UC1,TwoM.data);// source slice of TwoM.
MAs3DPlane.copyTo(OneTwo3DPlaneM);// copying TwoM slice to first slice

1
重塑应该可以工作。以下是一个示例。
//make sample 3d mat
Mat img = (Mat_<double>(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 9);

//reshape  - creates a new mat header of 1 row without copying data
Mat result = img.reshape ( 0, 1 );

// declare vector and alloc size
std::vector<double> outVector;
outVector.reserve( result.cols );

//copy data
result.copyTo( outVector );

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接