21

I am creating a multi-dimensional MAT object, and would like to get the size of the object - e.g.,

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;

I believe the resulting MAT to be 10x10x9, and I'd like to confirm, but the COUT statement gives:

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

I was hoping to see either:

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

Or:

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

How can I get the dimensionality of this Mat object? I didn't see any methods in Mat::Mat or MatND

Pete
  • 2,336
  • 2
  • 16
  • 23

3 Answers3

38

You just found yourself one of the many flaws of the OpenCV C++ API.

If you take a look at the source code of OpenCV, version 2.4.6.1, you will realize cv::Mat::size is a member object of type cv::Mat::MSize, which is defined as

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;
};

Thus cv::Mat::size() actually refers to cv::Mat::MSize::operator ()(), whose return type Size is defined as

typedef Size_<int> Size2i;
typedef Size2i Size;

Quoting from the OpenCV manual, Size is a

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

In other words, Size is only suitable for 2D matrices.

Fortunately all hope is not lost as you can use cv::Mat::MSize::operator [](int i) to get the size of the matrix along its i-th dimension.

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

Community
  • 1
  • 1
brunocodutra
  • 2,329
  • 17
  • 23
  • 3
    What a thorough and detailed answer! Thanks! As it turns out, I'm finding it easier to use Vector to store each slice for now (I am not doing much cross-slice indexing). Accepted and voted up. – Pete Sep 18 '13 at 22:00
  • @Pete Experience eventually taught me to avoid `cv::Mat` at all reasonable [read almost any] costs. As an advice, if you don't know yet of the existence of `cv::Mat_<>`, I suggest you take a look at it [here](http://docs.opencv.org/modules/core/doc/basic_structures.html?highlight=mat_#Mat_). It hides the most outrageous flaws of the `cv::Mat` API and helped me putting up with OpenCV this long. – brunocodutra Sep 18 '13 at 22:06
  • OK. Good to know! Is it the access operators you prefer? Any other benefits? I see you can provide vectors (e.g., Vec3b)'s as the base types; I assume this breaks Filter2D, and others? But I will investigate. The documentation for all of this seems... sparse. – Pete Sep 18 '13 at 22:28
  • 2
    @Pete `cv::Mat_<>` inherits `cv::Mat` and is thus implicitly convertible to it, so it can seamlessly replace `cv::Mat` in most contexts. The main benefit of it is being staticaly typed, in the sense the compiler is left to deal with the checking of the underlying data type (doing not more than is expected from it, one might add). That means no more nonsense like asserts to check the underlying data type of a matrix, dynamic function overload techniques, and the list goes on. Anybody who has already used OpenCV can testify these are the most error prone caveats. – brunocodutra Sep 18 '13 at 22:43
  • @brunocodutra: Compiling OpenCV requires Eigen. Do you know what is the relationship between `cv::Mat` or `cv::Mat_<>` and `Eigen::Matrix`? – Siyuan Ren Sep 19 '13 at 01:06
  • @C.R. AFAIK Eigen is optional for some linear algebra algorithms. I never really used both together, but check [this](http://stackoverflow.com/questions/14783329/opencv-cvmat-and-eigenmatrix) and [this](http://stackoverflow.com/questions/16451111/cvmat-conversion-to-eigen-matrix-and-back) out – brunocodutra Sep 19 '13 at 01:15
  • @brunocodutra: I thought cv::Mat was a wrapper for Eigen::Matrix. Thanks for your links; it's very helpful. – Siyuan Ren Sep 19 '13 at 01:51
  • The last paragraph of your answer isn't quite right; The functions `int& operator[](int i);` and `const int& operator[](int i) const;` return the size of the matrix along its `i`-th dimension, cf. @Sameer's answer. Since your answer is the accepted one, maybe you could update your answer to avoid confusing other readers unnecessarily. Otherwise a good answer. – nils Jul 14 '16 at 13:13
  • @brunocodutra: [how] does using `cv::Mat_` help with the size issue? – Adi Shavit Jun 14 '17 at 08:52
  • 1
    @AdiShavit it doesn't, that was an advice to the OP, who mentioned he's started migrating to the type-erased `cv::Mat` and thus I suggested the statically typed `cv::Mat_<>` should be preferred whenever that makes sense. – brunocodutra Jun 14 '17 at 09:20
14

OpenCV 2.4.9 deals with multi-dimensional sizes just fine. The struct cv::Mat::MSize can stores and return multiple dimensions. The data member cv::Mat::size is of the type cv::Mat::MSize. This code will enumerate the dimensions for you:

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;

The output is:

4       3,4,3,6,
Sameer
  • 2,435
  • 23
  • 13
  • I am reading the OpenCV2.4.9 document. It writes: C++: Size Mat::size() const The method returns a matrix size: Size(cols, rows) . When the matrix is more than 2-dimensional, the returned size is (-1, -1). – user1914692 Dec 17 '14 at 02:01
  • I guess the documentation hasn't caught up with the API change. Did the example code above work for you? – Sameer Jan 15 '15 at 20:04
  • 1
    thanks. The method .size() can only work for 2-dimension. But your example, .size (member variable) can work for multiple dimensions. – user1914692 Jan 15 '15 at 22:16
  • This is a better answer actually. Use thisMultiDimenonalMatrix.size, then you get the all dimensional size! do not use .size()!! – user1914692 Aug 17 '15 at 16:09
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;
}
Steve132
  • 11
  • 1