2014-10-31 256 views
23

我是OpenCV的新手。最近,我遇到了麻烦,发现OpenCV函数将从Mat转换为Array。我用OpenCV API中提供的.ptr和.at方法进行了研究,但我无法获得正确的数据。我想直接从Mat转换为Array(如果可用,如果不是Vector)。我需要OpenCV功能,因为代码必须在Vivado HLS中进行高级综合。请帮忙。在OpenCV中将矩阵转换为阵列/向量

回答

55

如果Mat mat的内存是连续的(其所有的数据是连续的),你可以直接得到它的数据为一维数组:

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

否则,您必须按行来获取其数据行,例如以二维数组:

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:

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

PS:如果你使用std::vector,在这里你可以这样做会更容易为cv::Mat S的其他类型,如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); 
    } 
} 
+3

更好如果使用一个std: :向量。用裸指针你也可以释放内存。 – madduci 2015-01-19 12:20:32

+2

@blackibiza好点。更新了使用'std :: vector'的答案。 :-) – herohuyongtao 2015-01-19 13:00:45

+0

我猜''mat.cols'应该乘以'mat.channels'复制数据'array.insert'。使用float作为模板参数时,也可以省略从'uchar *'到'float *'的转换:'mat.ptr ' – 2017-03-07 09:36:59

3

而不是逐行获取图像,您可以直接将其放入数组。对于CV_8U类型的图像,可以使用字节数组,对于其他类型的检查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 
+2

您的解决方案不适用于C++,仅适用于Java。 – YuZ 2015-06-02 13:33:15

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

虽然这段代码可能会回答这个问题,但提供关于此代码为何和/或如何回答此问题的其他上下文会提高其长期价值。 – ryanyuyu 2016-03-16 21:33:39

4

这里是另一个可能的解决方案假设矩阵有一列(您可以通过reshape重塑原来垫一个MAT):

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

为什么[此代码](https://i.stack.imgur.com/RQ8fi.png)无法正常运行? – yode 2017-12-06 02:41:35

0

无的提供的例子在这里的工作一般情况下,这是N维矩阵。任何使用“行”的东西只是假设列和行,4维矩阵可能会有更多。

下面是一些示例代码复制的非连续的N维矩阵划分成连续的存储器流 - 然后将其转换回简历::垫

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