Is Opencv Matrix Data Guaranteed to Be Continuous

OpenCV Mat data member access

Mat data is an uchar*. If you have a, say, float matrix CV_32FC1, you need to access data as float.

You can do in different ways, not necessarily using casting:

#include <opencv2\opencv.hpp>
using namespace cv;

int main()
{
cv::Mat matF(3, 3, CV_32F);
randu(matF, Scalar(0), Scalar(10));

int rowIdx = 1;
int colIdx = 1;

// 1
float f1 = matF.at<float>(rowIdx, colIdx);

// 2
float* fData2 = (float*)matF.data;
float f2 = fData2[rowIdx*matF.step1() + colIdx];

// 3
float* fData3 = matF.ptr<float>(0);
float f3 = fData3[rowIdx*matF.step1() + colIdx];

// 4
float* fData4 = matF.ptr<float>(rowIdx);
float f4 = fData4[colIdx];

// 5
Mat1f mm(matF); // Or directly create like: Mat1f mm(3, 3);
float f5 = mm(rowIdx, colIdx);

// f1 == f2 == f3 == f4 == f5

return 0;
}

Notes

  • it's better to use step1() instead of cols when accessing directly data through pointers, since the image may not be continuous. Check here for more details.
  • The snippet is adapted from this my other answer

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::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.

Copying OpenCV mat to a known address with memcpy

Ok. I solved this issue ;

    Mat img = Mat(height, width, CV_8UC4, address);
cv::imshow("Image from GM", img);
// same image copy to buffer back;
memcpy(&address[0], &img.data[0], width*height*4.);

What is the most efficient matrix representation in C++?

Unless you do some weird stuff (see here for details), data in a Mat are guaranteed to be continuous. You can think of a Mat as a lightweight wrapper over a float* (or other types) that allows easier access to the data. So it's as efficient as a pointer, but with a few nice-to-have abstractions.

If you need to efficiently load/save from/to file, you can save the Mat in binary format using matread and matwrite.

OpenCV : What is the real performance difference between continuous and non-continuous matrices?

Well, it mostly depends on how you'll use your matrices. If you're doing a lot of "jumping" anyways - it won't make much difference, but in 'continuous' use cases, it will matter a few dozens of percents.

The following example (which simply shifts the matrices values) gives me an output:

image.isContinuous() = 1
roi.isContinuous() = 0
image: 0.0162504 s
roi: 0.0219723 s
Sanity check: OK

Which is about 30% difference. Your millage will vary depending on the hardware and actual use cases.

Source (notice how the first loop is much simpler in that case):

#include <opencv2/core/core.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main( int argc, char** argv )
{
int cols = 4096;
int rows = 4096;
int scale = 2;

Mat image(rows, cols, CV_8UC1);
Mat image_big(rows * scale, cols * scale, CV_8UC1);
Mat roi = image_big(Rect(0, 0, cols, rows));

randu(image, 0, 255);
image.copyTo(roi);

cout << "image.isContinuous() = " << image.isContinuous() << "\n" << "roi.isContinuous() = " << roi.isContinuous() << endl;

{
cout << "image: ";
double start = getTickCount();
for (int i = 1; i < image.total(); i++)
{
image.data[i - 1] = image.data[i];
}
cout << (getTickCount() - start)/getTickFrequency() << " s" << endl;
}

{
cout << "roi: ";
double start = getTickCount();
for (int y = 0; y < roi.cols; y++)
{
if (y != 0) {
roi.ptr<char>(y-1)[roi.cols-1] = roi.ptr<char>(y)[0];
}

for (int x = 1; x < roi.rows; x++)
{
roi.ptr<char>(y)[x - 1] = roi.ptr<char>(y)[x];
}
}
cout << (getTickCount() - start)/getTickFrequency() << " s" << endl;
}

cout << "Sanity check: " << (countNonZero(image - roi) ? "FAIL" : "OK") << endl;
}

convert Mat to memory in opencv by using c++

Mat is also two dimensional array covered by class. If you need data in it, you can access it with Mat::at(int x, int y) method. Or copy raw data:

cv::Mat matrix;
float *new_data = new float[matrix.rows*matrix.cols];
float *p = new_data;
for(int i = 0; i < matrix.rows; i++){
memcpy(p, matrix.ptr(i), matrix.cols*sizeof(float));
p += matrix.cols;
}


Related Topics



Leave a reply



Submit