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.
Mat to vectorcv::Mat flat = image. reshape(1, image. total()*image. channels()); std::vector<uchar> vec = image.
In OpenCV the main matrix class is called Mat and is contained in the OpenCV-namespace cv. This matrix is not templated but nevertheless can contain different data types. These are indicated by a certain type-number. Additionally, OpenCV provides a templated class called Mat_, which is derived from Mat.
The Mat class of OpenCV library is used to store the values of an image. It represents an n-dimensional array and is used to store image data of grayscale or color images, voxel volumes, vector fields, point clouds, tensors, histograms, etc.
CV_32F defines the depth of each element of the matrix, while. CV_32FC1 defines both the depth of each element and the number of channels.
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:
imread()
, clone()
, or a constructor will always be continuous.Please check out this code snippet for demonstration.
uchar * arr = image.isContinuous()? image.data: image.clone().data; uint length = image.total()*image.channels();
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
.
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);
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.
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.
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()
.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With