In OpenCV I have cv::Mat object.
It is one channel with the format CV_8UC1 = unsigned char.
It is continuous, which means the data are stored in one place and rows by rows.
I know the numbers of rows and columns of the cv::Mat object.
I have 2D std::vector of size
vector< vector > vec1(rows, vector(img.cols));
How could I assign the data of cv::Mat object to the std::vector without copying.
For copying it is easy to create a for-loop or maybe use vector::assign with an iterator.
The storage of the data is the same between cv::Mat and 2D-std::vector. Both have an internal pointer to the data (vector::data and Mat::ptr) but vector::data can't be set to the value of Mat::ptr.
Currently, I have the code with copying the data:
cv::Mat img = cv::imread("test.tif");
cv::cvtColor(img, img, cv::COLOR_BGR2GRAY);
vector< vector<double> > vec1(img.rows, vector<double>(img.cols));
for(int i=0; i < img.rows; ++i)
for(int j=0; j < img.cols; ++j)
vec1.at(i).at(j) = img.at<double>(i, j);
Thanx.
The short answer is you shouldn't try to share data between std::vector and cv::Mat because both take ownership of the data and cv::Mat can share it with other cv::Mat objects and that will become pretty unmanageable pretty fast.
But cv::Mat can share data with another cv::Mat and you can extract one or more rows (or columns) and have a partial shared data with another cv::Mat like this:
cv::Mat img;
//.. read the img
//first row
cv::Mat row0 = img(cv::Range(0,1), cv::Range(0,img.cols));
//the 4'th column is shared
cv::Mat column3 = img(cv::Range(0,img.rows), cv::Range(3,4));
//you can set new data into column3/row0 and you'll see it's shared:
//row0.setTo(0);
//column3.setTo(0);
Now you can create a std::vector<cv::Mat> where each Mat will be a row in the original image (and it will share the data with the original image), or use it in whatever way you need.
Using OpenCV 3.4.1 in C++ I have a 2 dimensional Mat array object. These first few lines just explain essentially how the data are obtained to fill this Mat object ("dat")
int rows=NUMBER_OF_ROWS;
int cols=NUMBER_OF_COLS;
float* rawdata;
rawdata = new float[rows*cols];
fits_read_img(FP, TFLOAT, firstpixel, npixels, &nullval, rawdata, &anynull, &status);
Then creation of the Mat object from the raw data looks like this:
cv::Mat dat = cv::Mat(rows, cols, CV_32FC1, rawdata);
So far, so good, and I've used this Mat object "dat" for many things. But now I want to create a row average of the data. That should give me a rows x 1 vector, with every column of each row reduced into a single value, the average of all the values in that row. Something like this:
So OpenCV has a reduce() function. Here is how I've tried calling it:
std::vector<float> vec(rows);
cv::reduce(dat, vec, 1, cv::REDUCE_AVG, CV_32FC1);
which segfaults. If I go the other direction, I.E.
cv::reduce(dat, vec, 0, cv::REDUCE_AVG, CV_32FC1);
then I get a 1 x cols vector containing a column average. In fact, it gives me the exact equivalent of this:
int r, c;
float sum;
for (c=0; c<cols; c++) {
sum=0;
for (r=0; r<rows; r++) {
sum += dat.at<float>(r,c);
}
std::cout << "sum=" << ss << " avg=" << ss/r << std::endl;
}
So I could certainly use a brute-force method like this to get the row average that I want, but it seems I ought to be able to get it with a single cv::reduce() call.
What am I missing?
I am novice in OpenCV. Recently, I have troubles finding OpenCV functions to convert from Mat to Array. I researched with .ptr and .at methods available in OpenCV APIs, but I could not get proper data. I would like to have direct conversion from Mat to Array(if available, if not to Vector). I need OpenCV functions because the code has to be undergo high level synthesis in Vivado HLS. Please help.
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::Mats 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.
Can be done in two lines :)
Mat to array
uchar * arr = image.isContinuous()? image.data: image.clone().data;
uint length = image.total()*image.channels();
Mat to vector
cv::Mat flat = image.reshape(1, image.total()*image.channels());
std::vector<uchar> vec = image.isContinuous()? flat : flat.clone();
Both work for any general cv::Mat.
Explanation with a working example
cv::Mat image;
image = cv::imread(argv[1], cv::IMREAD_UNCHANGED); // Read the file
cv::namedWindow("cvmat", cv::WINDOW_AUTOSIZE );// Create a window for display.
cv::imshow("cvmat", image ); // Show our image inside it.
// flatten the mat.
uint totalElements = image.total()*image.channels(); // Note: image.total() == rows*cols.
cv::Mat flat = image.reshape(1, totalElements); // 1xN mat of 1 channel, O(1) operation
if(!image.isContinuous()) {
flat = flat.clone(); // O(N),
}
// flat.data is your array pointer
auto * ptr = flat.data; // usually, its uchar*
// You have your array, its length is flat.total() [rows=1, cols=totalElements]
// Converting to vector
std::vector<uchar> vec(flat.data, flat.data + flat.total());
// Testing by reconstruction of cvMat
cv::Mat restored = cv::Mat(image.rows, image.cols, image.type(), ptr); // OR vec.data() instead of ptr
cv::namedWindow("reconstructed", cv::WINDOW_AUTOSIZE);
cv::imshow("reconstructed", restored);
cv::waitKey(0);
Extended explanation:
Mat is stored as a contiguous block of memory, if created using one of its constructors or when copied to another Mat using clone() or similar methods. To convert to an array or vector we need the address of its first block and array/vector length.
Pointer to internal memory block
Mat::data is a public uchar pointer to its memory.
But this memory may not be contiguous. As explained in other answers, we can check if mat.data is pointing to contiguous memory or not using mat.isContinous(). Unless you need extreme efficiency, you can obtain a continuous version of the mat using mat.clone() in O(N) time. (N = number of elements from all channels). However, when dealing images read by cv::imread() we will rarely ever encounter a non-continous mat.
Length of array/vector
Q: Should be row*cols*channels right?
A: Not always. It can be rows*cols*x*y*channels.
Q: Should be equal to mat.total()?
A: True for single channel mat. But not for multi-channel mat
Length of the array/vector is slightly tricky because of poor documentation of OpenCV. We have Mat::size public member which stores only the dimensions of single Mat without channels. For RGB image, Mat.size = [rows, cols] and not [rows, cols, channels]. Mat.total() returns total elements in a single channel of the mat which is equal to product of values in mat.size. For RGB image, total() = rows*cols. Thus, for any general Mat, length of continuous memory block would be mat.total()*mat.channels().
Reconstructing Mat from array/vector
Apart from array/vector we also need the original Mat's mat.size [array like] and mat.type() [int]. Then using one of the constructors that take data's pointer, we can obtain original Mat. The optional step argument is not required because our data pointer points to continuous memory. I used this method to pass Mat as Uint8Array between nodejs and C++. This avoided writing C++ bindings for cv::Mat with node-addon-api.
References:
Create memory continuous Mat
OpenCV Mat data layout
Mat from array
Here is another possible solution assuming matrix have one column( you can reshape original Mat to one column Mat via reshape):
Mat matrix= Mat::zeros(20, 1, CV_32FC1);
vector<float> vec;
matrix.col(0).copyTo(vec);
None of the provided examples here work for the generic case, which are N dimensional matrices. Anything using "rows" assumes theres columns and rows only, a 4 dimensional matrix might have more.
Here is some example code copying a non-continuous N-dimensional matrix into a continuous memory stream - then converts it back into a Cv::Mat
#include <iostream>
#include <cstdint>
#include <cstring>
#include <opencv2/opencv.hpp>
int main(int argc, char**argv)
{
if ( argc != 2 )
{
std::cerr << "Usage: " << argv[0] << " <Image_Path>\n";
return -1;
}
cv::Mat origSource = cv::imread(argv[1],1);
if (!origSource.data) {
std::cerr << "Can't read image";
return -1;
}
// this will select a subsection of the original source image - WITHOUT copying the data
// (the header will point to a region of interest, adjusting data pointers and row step sizes)
cv::Mat sourceMat = origSource(cv::Range(origSource.size[0]/4,(3*origSource.size[0])/4),cv::Range(origSource.size[1]/4,(3*origSource.size[1])/4));
// correctly copy the contents of an N dimensional cv::Mat
// works just as fast as copying a 2D mat, but has much more difficult to read code :)
// see http://stackoverflow.com/questions/18882242/how-do-i-get-the-size-of-a-multi-dimensional-cvmat-mat-or-matnd
// copy this code in your own cvMat_To_Char_Array() function which really OpenCV should provide somehow...
// keep in mind that even Mat::clone() aligns each row at a 4 byte boundary, so uneven sized images always have stepgaps
size_t totalsize = sourceMat.step[sourceMat.dims-1];
const size_t rowsize = sourceMat.step[sourceMat.dims-1] * sourceMat.size[sourceMat.dims-1];
size_t coordinates[sourceMat.dims-1] = {0};
std::cout << "Image dimensions: ";
for (int t=0;t<sourceMat.dims;t++)
{
// calculate total size of multi dimensional matrix by multiplying dimensions
totalsize*=sourceMat.size[t];
std::cout << (t>0?" X ":"") << sourceMat.size[t];
}
// Allocate destination image buffer
uint8_t * imagebuffer = new uint8_t[totalsize];
size_t srcptr=0,dptr=0;
std::cout << std::endl;
std::cout << "One pixel in image has " << sourceMat.step[sourceMat.dims-1] << " bytes" <<std::endl;
std::cout << "Copying data in blocks of " << rowsize << " bytes" << std::endl ;
std::cout << "Total size is " << totalsize << " bytes" << std::endl;
while (dptr<totalsize) {
// we copy entire rows at once, so lowest iterator is always [dims-2]
// this is legal since OpenCV does not use 1 dimensional matrices internally (a 1D matrix is a 2d matrix with only 1 row)
std::memcpy(&imagebuffer[dptr],&(((uint8_t*)sourceMat.data)[srcptr]),rowsize);
// destination matrix has no gaps so rows follow each other directly
dptr += rowsize;
// src matrix can have gaps so we need to calculate the address of the start of the next row the hard way
// see *brief* text in opencv2/core/mat.hpp for address calculation
coordinates[sourceMat.dims-2]++;
srcptr = 0;
for (int t=sourceMat.dims-2;t>=0;t--) {
if (coordinates[t]>=sourceMat.size[t]) {
if (t==0) break;
coordinates[t]=0;
coordinates[t-1]++;
}
srcptr += sourceMat.step[t]*coordinates[t];
}
}
// this constructor assumes that imagebuffer is gap-less (if not, a complete array of step sizes must be given, too)
cv::Mat destination=cv::Mat(sourceMat.dims, sourceMat.size, sourceMat.type(), (void*)imagebuffer);
// and just to proof that sourceImage points to the same memory as origSource, we strike it through
cv::line(sourceMat,cv::Point(0,0),cv::Point(sourceMat.size[1],sourceMat.size[0]),CV_RGB(255,0,0),3);
cv::imshow("original image",origSource);
cv::imshow("partial image",sourceMat);
cv::imshow("copied image",destination);
while (cv::waitKey(60)!='q');
}
Instead of getting image row by row, you can put it directly to an array. For CV_8U type image, you can use byte array, for other types check here.
Mat img; // Should be CV_8U for using byte[]
int size = (int)img.total() * img.channels();
byte[] data = new byte[size];
img.get(0, 0, data); // Gets all pixels
byte * matToBytes(Mat image)
{
int size = image.total() * image.elemSize();
byte * bytes = new byte[size]; //delete[] later
std::memcpy(bytes,image.data,size * sizeof(byte));
}
You can use iterators:
Mat matrix = ...;
std::vector<float> vec(matrix.begin<float>(), matrix.end<float>());
cv::Mat m;
m.create(10, 10, CV_32FC3);
float *array = (float *)malloc( 3*sizeof(float)*10*10 );
cv::MatConstIterator_<cv::Vec3f> it = m.begin<cv::Vec3f>();
for (unsigned i = 0; it != m.end<cv::Vec3f>(); it++ ) {
for ( unsigned j = 0; j < 3; j++ ) {
*(array + i ) = (*it)[j];
i++;
}
}
Now you have a float array. In case of 8 bit, simply change float to uchar, Vec3f to Vec3b and CV_32FC3 to CV_8UC3.
If you know that your img is 3 channel, than you can try this code
Vec3b* dados = new Vec3b[img.rows*img.cols];
for (int i = 0; i < img.rows; i++)
for(int j=0;j<img.cols; j++)
dados[3*i*img.cols+j] =img.at<Vec3b>(i,j);
If you wanna check the (i,j) vec3b you can write
std::cout << (Vec3b)img.at<Vec3b>(i,j) << std::endl;
std::cout << (Vec3b)dados[3*i*img.cols+j] << std::endl;
Since answer above is not very accurate as mentioned in its comments but its "edit queue is full", I have to add correct one-liners.
Mat(uchar, 1 channel) to vector(uchar):
std::vector<uchar> vec = (image.isContinuous() ? image : image.clone()).reshape(1, 1); // data copy here
vector(any type) to Mat(the same type):
Mat m(vec, false); // false(by default) -- do not copy data
I am trying to understand the following piece of code Take from: Opencv Mat
and more precisely this part:
Mat labels(0, 1, CV_32FC1);
Mat trainingData(0, dictionarySize, CV_32FC1);
From what I understand is that labels is equivalent to std::vector<float> and trainingData is equivalent to std::vector<std::vector<float>> and where std::vector<float> is of dimension dictionarySize. Is that correct?
I am asking this question because I want to convert bowDescriptor1 which is a MAT to std::vector<float>
Convert bowDescriptor1to vector:
std::vector<float> data;
for(size_t r = 0; r < bowDescriptor.rows;r++)
{
for(size_t c = 0; c < bowDescriptor.cols;c++)
{
data.push_back(bowDescriptor.at<float>(r,c));
}
}
Without testing:
from documentation you can see that bowDescriptor seems to be a matrix of size 1 x dictionarySize http://docs.opencv.org/modules/features2d/doc/object_categorization.html#bowimgdescriptorextractor-descriptorsize
so you have to go through that matrix and save each element (float) to your vector<float>
try this code:
std::vector<float> currentBowDescriptor;
for(int col = 0; col < bowDescriptor1.cols; ++col)
{
currentBowDescriptor.push_back(bowDescriptor.at<float>(0,col));
}
that's it. push_back those currentBowDescriptor s to another vector if you want.
If you want to save some computation time, you can even initialize the currentBowDescriptor in advance since you know the number of descriptor values (dictionarySize) and access those elements instead of pushing back.
hope this helps.
I would like to store all my precalculated Keypoints/decriptors of several images in a Mat list/structure or something, so I could be able to use them later to match them with others image descriptors.
Do you have an idea?
Apparently, there is a way to use
List<Mat>
but i dont know how.
You store the descriptor of one image in one Mat variable. So, basically you have one Mat for one descriptors. So, if you have 100 descriptors then, all of these decriptors should be present in a single Mat. You can do it as following:
Step-1: Declare a vector of Mat type.
vector<Mat> allDescriptors;
Step-2: Then find descriptors for each image and store it in Mat format
Mat newImageDescriptor;
Step-3: finally, push the descriptor calculated above into the vector.
allDescriptors.push_back(newImageDescriptor);
Repeat step-2 & 3 for all of your images
Now, you can access them as following:
You can access the data in vector as you do it in case of arrays
so allDescriptors[0] will give you the first descriptor in Mat format
So, by using a for loop, you can access all of your descriptors.
for(int i=0; i<allDescriptors.size(); i++)
{
Mat accessedDescriptor;
allDescriptors[i].copyTo(accessedDescriptor);
}
If your elements are stored in contiguous array you can assign them to a list at once with:
#include <list>
std::list<Mat> l;
l.assign( ptr, ptr + sz); // where ptr is a pointer to array of Mat s
// and sz is the size of array
Create precalculated elements:
Mat mat1;
Mat mat2;
And the list of elements of this type:
#include <list>
std::list<Mat> l;
Add elements to list:
l.push_back( mat1);
l.push_back( mat2)
note: there are other modifiers that you can use to insert elements. You will find a description of them here. There are other containers which usage you can consider. Selection of appropriate container is very important. You have to take into account the operations that will be crucial to you, which will be called most often.
This is regarding to your another question of copying the vector<Mat> to another vector<Mat>
Lets say you have one vector vector<Mat> des1 and you want to copy it to vector<Mat> des2 then you should do the following:
for(int i=0; i<des1.size(); i++)
{
des1[i].copyTo(des2[i]);
}
Remember that vector<Mat> is something like an arrya of Mat. So, how can you copy a vector to another vector by CopyTo which is used to copy a Matrix.