10

I have a fairly simple question: how to take one row of cv::Mat and get all the data in std::vector? The cv::Mat contains doubles (it can be any simple datatype for the purpose of the question).

Going through OpenCV documentation is just very confusing, unless I bookmark the page I can not find a documentation page twice by Googling, there's just to much of it and not easy to navigate.

I have found the cv::Mat::at(..) to access the Matrix element, but I remember from C OpenCV that there were at least 3 different ways to access elements, all of them used for different purposes... Can't remember what was used for which :/

So, while copying the Matrix element-by-element will surely work, I am looking for a way that is more efficient and, if possible, a bit more elegant than a for loop for each row.

penelope
  • 8,251
  • 8
  • 45
  • 87
  • I think it's a duplicated question: http://stackoverflow.com/questions/5405070/cvmat-to-stdvector-conversion – piokuc Mar 20 '12 at 16:00
  • @piokuc I saw that one, but that was platform specific (Visual Studio), for OpenCV 2.0 (older version than today) *and* concerning a specific `cv::Mat` wrapper `cv::Mat_` – penelope Mar 20 '12 at 16:22

5 Answers5

33

It should be as simple as:

m.row(row_idx).copyTo(v);

Where m is cv::Mat having CV_64F depth and v is std::vector<double>

Andrey Kamaev
  • 29,582
  • 6
  • 94
  • 88
  • wow this is great. But, since I actually needed to create a new vector and fill it with data, I think just giving the pointers to the vector constructor will work better in this case. – penelope Mar 21 '12 at 15:04
  • this should be the answer. So simple! – trozzel Jun 12 '21 at 16:32
17

Data in OpenCV matrices is laid out in row-major order, so that each row is guaranteed to be contiguous. That means that you can interpret the data in a row as a plain C array. The following example comes directly from the documentation:

// compute sum of positive matrix elements
// (assuming that M is double-precision matrix)
double sum=0;
for(int i = 0; i < M.rows; i++)
{
    const double* Mi = M.ptr<double>(i);
    for(int j = 0; j < M.cols; j++)
        sum += std::max(Mi[j], 0.);
}

Therefore the most efficient way is to pass the plain pointer to std::vector:

// Pointer to the i-th row
const double* p = mat.ptr<double>(i);

// Copy data to a vector.  Note that (p + mat.cols) points to the
// end of the row.
std::vector<double> vec(p, p + mat.cols);

This is certainly faster than using the iterators returned by begin() and end(), since those involve extra computation to support gaps between rows.

Ferdinand Beyer
  • 64,979
  • 15
  • 154
  • 145
6

From the documentation at here, you can get a specific row through cv::Mat::row, which will return a new cv::Mat, over which you can iterator with cv::Mat::begin and cv::Mat::end. As such, the following should work:

cv::Mat m/*= initialize */;
// ... do whatever...
cv::Mat first_row(m.row(0));
std::vector<double> v(first_row.begin<double>(), first_row.end<double>());

Note that I don't know any OpenCV, but googling "OpenCV mat" led directly to the basic types documentation and according to that, this should work fine.

The matrix iterators are random-access iterators, so they can be passed to any STL algorithm, including std::sort() .

This is also from the documentiation, so you could actually do this without a copy:

cv::Mat m/*= initialize */;
// ... do whatever...
// first row          begin              end
std::vector<double> v(m.begin<double>(), m.begin<double>() + m.size().width);

To access more than the first row, I'd recommend the first snippet, since it will be a lot cleaner that way and there doesn't seem to be any heavy copying since the data types seem to be reference-counted.

Xeo
  • 129,499
  • 52
  • 291
  • 397
  • Did you mean `m.size().width` in your second example? Note that OpenCV matrices are stored row-major and you are adding the number of *rows* when you should probably be adding the number of *columns*. – Ferdinand Beyer Mar 20 '12 at 16:20
  • @Ferdinand: Woops, yes, that was indeed what I intended. Thanks for the hint. – Xeo Mar 20 '12 at 16:23
  • 2
    Instead of `m.size().width`, you can directly use `m.cols`. – Ferdinand Beyer Mar 20 '12 at 16:28
2

You can also use cv::Rect

m(cv::Rect(0, 0, 1, m.cols)) 

will give you first row.

matrix(cv::Rect(x0, y0, len_x, len_y);

means that you will get sub_matrix from matrix whose upper left corner is (x0,y0) and size is (len_x, len_y). (row,col)

smttsp
  • 4,011
  • 3
  • 33
  • 62
0

I think this works,

an example :

Mat Input(480, 720, CV_64F, Scalar(100));

cropping the 1st row of the matrix:

Rect roi(Point(0, 0), Size(720, 1));

then:

std::vector<std::vector<double> > vector_of_rows;

vector_of_rows.push_back(Input(roi));