How do i get the size of a multi-dimensional cv::Mat? (Mat, or MatND)
Asked Answered
B

3

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

Burger answered 18/9, 2013 at 20:58 Comment(0)
M
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

Mut answered 18/9, 2013 at 21:58 Comment(10)
What a thorough and detailed answer! Thanks! As it turns out, I'm finding it easier to use Vector<Mat> to store each slice for now (I am not doing much cross-slice indexing). Accepted and voted up.Burger
@Burger 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. It hides the most outrageous flaws of the cv::Mat API and helped me putting up with OpenCV this long.Mut
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.Burger
@Burger 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.Mut
@brunocodutra: Compiling OpenCV requires Eigen. Do you know what is the relationship between cv::Mat or cv::Mat_<> and Eigen::Matrix?Megalocardia
@C.R. AFAIK Eigen is optional for some linear algebra algorithms. I never really used both together, but check this and this outMut
@brunocodutra: I thought cv::Mat was a wrapper for Eigen::Matrix. Thanks for your links; it's very helpful.Megalocardia
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.Jephthah
@brunocodutra: [how] does using cv::Mat_ help with the size issue?Sororate
@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.Mut
P
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,
Plattdeutsch answered 20/11, 2014 at 4:12 Comment(4)
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).Ozone
I guess the documentation hasn't caught up with the API change. Did the example code above work for you?Plattdeutsch
thanks. The method .size() can only work for 2-dimension. But your example, .size (member variable) can work for multiple dimensions.Ozone
This is a better answer actually. Use thisMultiDimenonalMatrix.size, then you get the all dimensional size! do not use .size()!!Ozone
G
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;
}
Germanize answered 14/1, 2015 at 11:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.