2014-10-31 16 views
23

Ich bin Anfänger in OpenCV. Vor kurzem habe ich Probleme, OpenCV-Funktionen zu finden, um von Mat zu Array zu konvertieren. Ich recherchierte mit .ptr und .at Methoden in OpenCV APIs, aber ich konnte nicht die richtigen Daten bekommen. Ich hätte gerne eine direkte Konvertierung von Mat zu Array (falls verfügbar, wenn nicht zu Vector). Ich brauche OpenCV-Funktionen, da der Code in Vivado HLS einer Synthese auf hohem Niveau unterzogen werden muss. Bitte helfen Sie.Mat in Array/Vektor in OpenCV konvertieren

Antwort

55

Wenn der Speicher des Mat mat kontinuierlich (alle seine Daten kontinuierlich), können Sie direkt ihre Daten an einem 1D-Array erhalten:

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

Ansonsten haben Sie für Zeile seine Datenreihe zu erhalten, z.B zu einem 2D-Array:

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: Es wird leichter, wenn Sie mit std::vector, wo Sie so tun können:

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: Für cv::Mat s Andere Typen, wie CV_32F, sollten Sie so machen:

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

besser, wenn Sie eine std: :Vektor. Mit nackten Zeigern müssen Sie auch Speicher freigeben. – madduci

+2

@blackibiza Guter Punkt. Die Antwort wurde aktualisiert, um 'std :: vector' zu verwenden. :-) – herohuyongtao

+0

Ich vermute, dass 'mat.cols' beim Kopieren von Daten mit' array.insert' mit 'mat.channels' multipliziert werden sollte. Auch die Konvertierung von 'uchar *' nach 'float *' kann weggelassen werden, wenn float als Template-Parameter verwendet wird: 'mat.ptr ' –

3

Anstatt das Bild zeilenweise abzurufen, können Sie es direkt in ein Array einfügen. Für CV_8U Typ Bild, können Sie Byte-Array verwenden, für andere Typen überprüfen Sie 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

Ihre Lösung funktioniert nicht für C++, nur für Java. – YuZ

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

Während dieser Code die Frage beantworten kann, verbessert einen zusätzlichen Kontext, warum und/oder wie dieser Code die Frage beantwortet, seinen langfristigen Wert. – ryanyuyu

4

Hier ist eine weitere mögliche Lösung unter der Annahme, Matrix hat eine Spalte (können Sie Original-Matte auf eine Spalte Mat über reshape umformen):

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

Warum [dieser Code] (https://i.stack.imgur.com/RQ8fi.png) kann nicht normal ausgeführt werden? – yode

0

Keine der angegebenen Beispiele hier arbeiten für die generischer Fall, die N-dimensionale Matrizen sind. Alles, was "Zeilen" verwendet, nimmt nur Spalten und Zeilen an, eine 4-dimensionale Matrix könnte mehr haben.

Hier einige Beispiel-Code eine nicht-kontinuierliche N-dimensionale Matrix in einen kontinuierlichen Speicherstrom zu kopieren - wandelt sie dann wieder in ein 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'); 
} 
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