Converting a row of cv::Mat to std::vector
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.
Convert Mat to Array/Vector in OpenCV
If the memory of the Mat mat
is continuous (all its data is continuous), you can directly get its data to a 1D array:
std::vector<uchar> array(mat.rows*mat.cols*mat.channels());
if (mat.isContinuous())
array = mat.data;
Otherwise, you have to get its data row by row, e.g. to a 2D array:
uchar **array = new uchar*[mat.rows];
for (int i=0; i<mat.rows; ++i)
array[i] = new uchar[mat.cols*mat.channels()];
for (int i=0; i<mat.rows; ++i)
array[i] = mat.ptr<uchar>(i);
UPDATE: It will be easier if you're using std::vector
, where you can do like this:
std::vector<uchar> array;
if (mat.isContinuous()) {
// array.assign(mat.datastart, mat.dataend); // <- has problems for sub-matrix like mat = big_mat.row(i)
array.assign(mat.data, mat.data + mat.total()*mat.channels());
} else {
for (int i = 0; i < mat.rows; ++i) {
array.insert(array.end(), mat.ptr<uchar>(i), mat.ptr<uchar>(i)+mat.cols*mat.channels());
}
}
p.s.: For cv::Mat
s of other types, like CV_32F
, you should do like this:
std::vector<float> array;
if (mat.isContinuous()) {
// array.assign((float*)mat.datastart, (float*)mat.dataend); // <- has problems for sub-matrix like mat = big_mat.row(i)
array.assign((float*)mat.data, (float*)mat.data + mat.total()*mat.channels());
} else {
for (int i = 0; i < mat.rows; ++i) {
array.insert(array.end(), mat.ptr<float>(i), mat.ptr<float>(i)+mat.cols*mat.channels());
}
}
UPDATE2: For OpenCV Mat data continuity, it can be summarized as follows:
- Matrices created by
imread()
,clone()
, or a constructor will always be continuous. - The only time a matrix will not be continuous is when it borrows data (except the data borrowed is continuous in the big matrix, e.g. 1. single row; 2. multiple rows with full original width) from an existing matrix (i.e. created out of an ROI of a big mat).
Please check out this code snippet for demonstration.
OpenCV 2.3.1. cv::Mat to std::vector cast
It seems like you are trying to convert a two-dimensional 3x3 matrix into a one-dimensional vector. Not sure what result you're expecting from that, but you probably want to convert a row of the matrix into a vector. You can use this by giving the vector constructor a pointer to the row data:
int *p = eye.ptr<int>(0); // pointer to row 0
std::vector<int> vec(p, p+eye.cols); // construct a vector using pointer
Converting a cv::Mat into a vectorint
cv::Mat
has a conversion operator to std::vector
, provided the vector has the proper data type.
cv::Mat m = cv::Mat::eye(3, 3, CV_8UC1);
std::vector<uchar> v = m.reshape(0, 1);
cv::Mat_ to std::vector conversion
No, there are new constructors from rvalues in C++0x, but nothing like the one used here.
If isContinuous() means that all vales are the same, you could possibly use vector<_Tp>((size_t)(rows + cols - 1), *(_Tp*)data)
to make copies of the first value.
Your iterator version seems correct otherwise.
Need a faster way to convert a cv::Mat into 1 dimensional vector form
assuming that the data in your Mat is continuous, Mat::reshape() for the win.
and it's almost for free. only rows/cols get adjusted, no memory moved. i.e, mat = mat.reshape(1,1)
would make a 1d float array of it.
From Mat to vectorVec3f and the other way around without corrupting the image
Let's use a small random image for demonstration:
// Generate random input image
cv::Mat image(5, 5, CV_8UC3);
cv::randu(image, 0, 256);
Option 1
Since the input is CV_8UC3
(i.e. each element is a cv::Vec3b
) and we want the elements as cv::Vec3f
, we first need to use convertTo
, to convert the Mat
to CV_32FC3
. We store the result in a temporary matrix, and for convenience (since we know the element type) we can explicitly use cv::Mat3f
.
// First convert to 32bit floats
cv::Mat3f temp;
image.convertTo(temp, CV_32FC3);
Now we can just use Mat
iterators to initialize the vector.
// Use Mat iterators to construct the vector.
std::vector<cv::Vec3f> v1(temp.begin(), temp.end());
Option 2
The previous option ends up allocating a temporary array. With a little creativity, we can avoid this.
As it turns out, it is possible to create a cv:Mat
header wrapping a vector, sharing the underlying data storage.
We begin by crating an adequately sized vector:
std::vector<cv::Vec3f> v2(image.total());
The Mat
created from such vector will have 1 column, and as many rows as there are elements. Therefore, we'll reshape
our input matrix to identical shape, and then use convertTo
, to write directly to the vector.
image.reshape(3, static_cast<int>(image.total())).convertTo(v2, CV_32FC3);
Whole program:
#include <opencv2/opencv.hpp>
#include <vector>
template<typename T>
void dump(std::string const& label, T const& data)
{
std::cout << label << ":\n";
for (auto const& v : data) {
std::cout << v << " ";
}
std::cout << "\n";
}
int main()
{
// Generate random input image
cv::Mat image(5, 5, CV_8UC3);
cv::randu(image, 0, 256);
// Option 1
// ========
// First convert to 32bit floats
cv::Mat3f temp;
image.convertTo(temp, CV_32FC3);
// Use Mat iterators to construct the vector.
std::vector<cv::Vec3f> v1(temp.begin(), temp.end());
// Option 2
// ========
std::vector<cv::Vec3f> v2(image.total());
image.reshape(3, static_cast<int>(image.total())).convertTo(v2, CV_32FC3);
// Output
// ======
dump("Input", cv::Mat3b(image));
dump("Vector 1", v1);
dump("Vector 2", v2);
return 0;
}
Sample output:
Input:
[246, 156, 192] [7, 165, 166] [2, 179, 231] [212, 171, 230] [93, 138, 123] [80, 105, 242] [231, 239, 174] [174, 176, 191] [134, 54, 234] [69, 25, 147] [24, 67, 124] [158, 203, 206] [89, 144, 210] [51, 31, 132] [123, 250, 234] [246, 204, 74] [111, 208, 249] [149, 234, 37] [55, 147, 143] [29, 214, 169] [215, 84, 190] [204, 110, 239] [216, 103, 137] [248, 173, 53] [221, 251, 29]
Vector 1:
[246, 156, 192] [7, 165, 166] [2, 179, 231] [212, 171, 230] [93, 138, 123] [80, 105, 242] [231, 239, 174] [174, 176, 191] [134, 54, 234] [69, 25, 147] [24, 67, 124] [158, 203, 206] [89, 144, 210] [51, 31, 132] [123, 250, 234] [246, 204, 74] [111, 208, 249] [149, 234, 37] [55, 147, 143] [29, 214, 169] [215, 84, 190] [204, 110, 239] [216, 103, 137] [248, 173, 53] [221, 251, 29]
Vector 2:
[246, 156, 192] [7, 165, 166] [2, 179, 231] [212, 171, 230] [93, 138, 123] [80, 105, 242] [231, 239, 174] [174, 176, 191] [134, 54, 234] [69, 25, 147] [24, 67, 124] [158, 203, 206] [89, 144, 210] [51, 31, 132] [123, 250, 234] [246, 204, 74] [111, 208, 249] [149, 234, 37] [55, 147, 143] [29, 214, 169] [215, 84, 190] [204, 110, 239] [216, 103, 137] [248, 173, 53] [221, 251, 29]
Issues with your Code
In
src->assign(in->datastart, in->dataend);
Elements of
src
areVec3f
, howeverdatastart
anddataend
are pointers touchar
.This will have several consequences. First of all, since
in
isCV_8UC3
, there will be 3x as many elements. Also, each of theVec3f
instances will only have the first entry set, the other 2 will be 0.In
src->insert(src->end(), in->ptr<Vec3f>(i), in->ptr<Vec3f>(i)+cols);
Recall that you have already initialized
src
asvector<Vec3f>(rows * cols);
-- i.e. the vector already has as many elements as there are pixels in the source image. However, in the loop you keep adding further elements at the end. This means that the resulting vector will have twice as many elements, with the first half of them being zeros.Furthermore,
in
isCV_8UC3
, but you interpret the data ascv::Vec3f
. This means you take the byte values of 4 consecutive pixels and intepret this as a sequence of 3 32bit floating point numbers. The result can't be anything else than garbage.It also means that you end up accessing data outside the valid area, potentially past the end of the buffer.
In
cv::Mat(rows, cols, CV_8U, src, cv::Mat::AUTO_STEP)
...First of all,
src
holdsVec3f
elements, but you're creating theMat
asCV_8U
(which is also an issue, since you need to provide channel count here as well, so it's actually interpreted asCV_8UC1
). So not only would you have the wrong number of channels, they would contain garbage due to type mismatch.Even bigger issue is that you pass
src
as the 4th parameter. Now, this is a pointer to thestd::vector
instance, not to the actual data it holds. (It compiles, since the 4th parameter isvoid*
). That means you're actually interpreting the metadata of thevector
, along with a lot of other unknown data. Result is garbage at best (Or as you found out, SEGFAULTs, or potentially nasty security bugs).
Back to Mat
Note that it is possible to imshow
a floating point Mat
, assuming the values are normalized in range [0,1].
We can take advantage of the Mat
constructor that takes a vector
, and just reshape the resulting matrix back to the original shape.
cv::Mat result(cv::Mat(v2).reshape(3, image.rows));
Note that in this case, the underlying data storage is shared with the source vector
, hence you need to assure it remains in scope as long the the Mat
does. If you do not wish to share the data, simply pass true
as a second parameter to the constructor.
cv::Mat result(cv::Mat(v2, true).reshape(3, image.rows));
Of course, if you want to go back to CV_8UC3
, that's as simple as adding a convertTo
. In this case there's no need to copy the vector data, since the data type changes and new storage array will allocated automatically.
cv::Mat result;
cv::Mat(v2).reshape(3, image.rows).convertTo(result, CV_8UC3);
Related Topics
How to Write a Stateful Allocator in C++11, Given Requirements on Copy Construction
Ad Hoc Polymorphism and Heterogeneous Containers with Value Semantics
Using 'Const' in Class's Functions
Counting the Number of Lines in a Text File
How to Check If a Process Has the Administrative Rights
Increment Void Pointer by One Byte? by Two
What C++ Library Should I Use to Implement a Http Client
Sort Based on Multiple Things in C++
Should I Use Printf in My C++ Code
Portable Compare and Swap (Atomic Operations) C/C++ Library
How to Calculate Perspective Transform for Opencv from Rotation Angles
How to Implement Lock Free Map in C++
How Can It Be Useful to Overload the "Function Call" Operator
How to Iterate Over a Constant Vector
Error: Passing 'Const …' as 'This' Argument of '…' Discards Qualifiers
Is There a Downside to Declaring Variables with Auto in C++