如何获取多维 cv::Mat 的大小?(Mat 或 MatND)

21

我正在创建一个多维MAT对象,并希望获得对象的大小 - 例如,

const int sz[] = {10,10,9};
Mat temp(3,sz,CV_64F);
std::cout << "temp.dims = " << temp.dims << " temp.size = " << temp.size() << " temp.channels = " << temp.channels() << std::endl;

我认为得到的MAT对象是10x10x9,但我想确认一下。然而,COUT语句给出了:

temp.dims = 3 temp.size = [10 x 10] temp.channels = 1

我希望看到以下之一:

temp.dims = 3 temp.size = [10 x 10 x 9] temp.channels = 1

或者:

temp.dims = 3 temp.size = [10 x 10] temp.channels = 9

如何获取此Mat对象的维数?我没有在Mat :: Mat或MatND中看到任何方法。

3个回答

38

你刚刚发现了OpenCV C++ API中的众多缺陷之一。

如果你查看OpenCV版本2.4.6.1的源代码,你会意识到cv::Mat::size是一个类型为cv::Mat::MSize的成员对象,它被定义为

struct CV_EXPORTS MSize
{
    MSize(int* _p);
    Size operator()() const;
    const int& operator[](int i) const;
    int& operator[](int i);
    operator const int*() const;
    bool operator == (const MSize& sz) const;
    bool operator != (const MSize& sz) const;

    int* p;
};
因此,cv::Mat::size() 实际上是指 cv::Mat::MSize::operator ()(),其返回类型 Size 的定义如下。
typedef Size_<int> Size2i;
typedef Size2i Size;

引用自OpenCV手册Size是一个模板类,用于指定图像或矩形的大小,该类包括两个成员:宽度和高度。

"Template class for specifying the size of an image or rectangle. The class includes two members called width and height."

换句话说,Size仅适用于二维矩阵。

幸运的是,您可以使用 cv::Mat::MSize::operator [](int i) 来获取沿着第i维度的矩阵大小

const int sz[] = {10,10,9}; 
cv::Mat temp(3,sz,CV_64F); 
std::cout << "temp.dims = " << temp.dims << "temp.size = [";
for(int i = 0; i < temp.dims; ++i) {
    if(i) std::cout << " X ";
    std::cout << temp.size[i];
}
std::cout << "] temp.channels = " << temp.channels() << std::endl;

temp.dims = 3 temp.size = [10 x 10 x 9] temp.channels = 1


3
多么详尽和详细的回答啊!谢谢!事实证明,我发现现在使用Vector<Mat>来存储每个切片更容易(我目前没有进行太多的跨切片索引)。已接受并投票支持。 - Pete
经验告诉我要尽可能避免使用cv::Mat。如果你还不知道cv::Mat_<>的存在,我建议你在这里看一下它(http://docs.opencv.org/modules/core/doc/basic_structures.html?highlight=mat_#Mat_)。它隐藏了`cv::Mat` API中最令人无法忍受的缺陷,并帮助我长时间使用OpenCV。 - brunocodutra
好的,知道了!您喜欢使用访问运算符吗?还有其他好处吗?我看到您可以提供向量(例如Vec3b)作为基本类型;我猜这会破坏Filter2D等功能?但我会进行调查。所有这些的文档似乎都很少。 - Pete
2
@Pete cv::Mat_<> 继承自 cv::Mat,因此可以无缝地替换 cv::Mat 在大多数情况下使用。它的主要优点是静态类型化,也就是说编译器负责检查底层数据类型(不做更多的期望之外的事情)。这意味着不再需要像断言一样检查矩阵的底层数据类型、动态函数重载技术等等。任何已经使用过 OpenCV 的人都可以证明这些是最容易出错的注意事项。 - brunocodutra
1
@AdiShavit,这并不是针对您的建议,而是针对OP的建议。他提到已经开始迁移到类型擦除的cv::Mat,因此我建议在合适的情况下应该优先选择静态类型的cv::Mat_<> - brunocodutra
显示剩余5条评论

14

OpenCV 2.4.9可以很好地处理多维尺寸。结构体cv::Mat::MSize可以存储和返回多个维度。数据成员cv::Mat::size的类型是cv::Mat::MSize。下面的代码将为您枚举这些维度:

const int sz[] = {3, 4, 3, 6};
cv::Mat bigm(4, sz, CV_8UC1);
cout << bigm.dims << '\t';
for (int i=0; i<bigm.dims; ++i)
  cout << bigm.size[i] << ',';
cout << endl;

输出结果为:
4       3,4,3,6,

我正在阅读OpenCV2.4.9文档。其中写道:C++: Size Mat::size() const。该方法返回矩阵的大小:Size(cols, rows)。当矩阵维数大于2时,返回的大小为(-1,-1)。 - user1914692
我猜文档还没有适应 API 的更改。上面的示例代码对你有用吗? - Sameer
1
方法 .size() 只能用于二维数组。但是你的例子中,.size (成员变量) 可以用于多维数组。谢谢。 - user1914692
这实际上是一个更好的答案。使用 thisMultiDimenonalMatrix.size,然后你就可以得到所有维度的大小!不要使用 .size()!! - user1914692

1
std::vector<size_t> getMatDims(const cv::Mat& m)
{
    std::vector<size_t> dims(m.dims);
    std::partial_sum(&m.step[0],&m.step[0]+m.dims,dims.begin(),[](size_t a,size_t b){ return a/b; });
    return dims;
}

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