在OpenCV中将Mat转换为Array/Vector。

时间:2022-12-20 04:22:41

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.

我是OpenCV的新手。最近,我在寻找OpenCV函数从Mat转换到数组时遇到了麻烦。我在OpenCV api中使用.ptr和.at方法进行了研究,但我无法获得正确的数据。我想要从Mat到数组的直接转换(如果可用的话,如果不是向量的话)。我需要OpenCV函数,因为在Vivado HLS中代码必须要进行高水平的合成。请帮助。

6 个解决方案

#1


60  

If the memory of the Mat mat is continuous (all its data is continuous), you can directly get its data to a 1D array:

如果Mat的内存是连续的(其所有数据都是连续的),您可以直接将其数据获取到一个1D数组:

std::vector<uchar> array(mat.rows*mat.cols);if (mat.isContinuous())    array = mat.data;

Otherwise, you have to get its data row by row, e.g. to a 2D array:

否则,您必须逐行获取它的数据,例如,获取一个2D数组:

uchar **array = new uchar*[mat.rows];for (int i=0; i<mat.rows; ++i)    array[i] = new uchar[mat.cols];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,您可以这样做:

std::vector<uchar> array;if (mat.isContinuous()) {  array.assign(mat.datastart, mat.dataend);} else {  for (int i = 0; i < mat.rows; ++i) {    array.insert(array.end(), mat.ptr<uchar>(i), mat.ptr<uchar>(i)+mat.cols);  }}

p.s.: For cv::Mats of other types, like CV_32F, you should do like this:

注。:对于cv::其他类型的垫子,比如CV_32F,你应该这样做:

std::vector<float> array;if (mat.isContinuous()) {  array.assign((float*)mat.datastart, (float*)mat.dataend);} else {  for (int i = 0; i < mat.rows; ++i) {    array.insert(array.end(), mat.ptr<float>(i), mat.ptr<float>(i)+mat.cols);  }}

#2


5  

Here is another possible solution assuming matrix have one column( you can reshape original Mat to one column Mat via reshape):

假设矩阵有一列,则有另一种可能的解决方案(你可以通过重塑将原来的Mat重塑为一列Mat):

Mat matrix= Mat::zeros(20, 1, CV_32FC1);vector<float> vec;matrix.col(0).copyTo(vec);

#3


3  

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.

不需要逐行获取图像,可以直接将它放到数组中。对于CV_8U类型的图像,您可以使用字节数组,其他类型请参阅这里。

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

#4


1  

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.

这里提供的示例中没有一个适用于一般情况,即N维矩阵。任何使用“行”的东西都假定只有列和行,一个四维矩阵可能有更多的列。

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

下面是一些将非连续n维矩阵复制到连续内存流的示例代码,然后将其转换回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://*.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');}

#5


0  

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));}

#6


0  

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 and Vec3f to Vec3b and CV_32FC3 to CV_8UC3

#1


60  

If the memory of the Mat mat is continuous (all its data is continuous), you can directly get its data to a 1D array:

如果Mat的内存是连续的(其所有数据都是连续的),您可以直接将其数据获取到一个1D数组:

std::vector<uchar> array(mat.rows*mat.cols);if (mat.isContinuous())    array = mat.data;

Otherwise, you have to get its data row by row, e.g. to a 2D array:

否则,您必须逐行获取它的数据,例如,获取一个2D数组:

uchar **array = new uchar*[mat.rows];for (int i=0; i<mat.rows; ++i)    array[i] = new uchar[mat.cols];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,您可以这样做:

std::vector<uchar> array;if (mat.isContinuous()) {  array.assign(mat.datastart, mat.dataend);} else {  for (int i = 0; i < mat.rows; ++i) {    array.insert(array.end(), mat.ptr<uchar>(i), mat.ptr<uchar>(i)+mat.cols);  }}

p.s.: For cv::Mats of other types, like CV_32F, you should do like this:

注。:对于cv::其他类型的垫子,比如CV_32F,你应该这样做:

std::vector<float> array;if (mat.isContinuous()) {  array.assign((float*)mat.datastart, (float*)mat.dataend);} else {  for (int i = 0; i < mat.rows; ++i) {    array.insert(array.end(), mat.ptr<float>(i), mat.ptr<float>(i)+mat.cols);  }}

#2


5  

Here is another possible solution assuming matrix have one column( you can reshape original Mat to one column Mat via reshape):

假设矩阵有一列,则有另一种可能的解决方案(你可以通过重塑将原来的Mat重塑为一列Mat):

Mat matrix= Mat::zeros(20, 1, CV_32FC1);vector<float> vec;matrix.col(0).copyTo(vec);

#3


3  

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.

不需要逐行获取图像,可以直接将它放到数组中。对于CV_8U类型的图像,您可以使用字节数组,其他类型请参阅这里。

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

#4


1  

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.

这里提供的示例中没有一个适用于一般情况,即N维矩阵。任何使用“行”的东西都假定只有列和行,一个四维矩阵可能有更多的列。

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

下面是一些将非连续n维矩阵复制到连续内存流的示例代码,然后将其转换回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://*.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');}

#5


0  

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));}

#6


0  

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 and Vec3f to Vec3b and CV_32FC3 to CV_8UC3