2011-02-17 23 views
31

Mi chiedo come convertire il tipo cv :: Mat Mat di OpenCV C++ in Qimage. Ho cercato in giro, ma non ho fortuna. Ho trovato un codice che converte l'IPlimage in Qimage, ma non è quello che voglio. Graziecome convertire un cv opencv :: Mat in qimage

+0

Sarà utile se esiste tale codice, ma ci sono alcuni problemi nella scrittura: il supporto cv :: Mat più tipi di dati che QImage, cv :: Mat supporta più canali, deve i dati essere copiata o avvolto intorno i dati originali ... –

risposta

2

cv :: Mat ha un operatore di conversione a IplImage, quindi se avete qualcosa che converte l'IplImage ad un QImage, basta usare che (o fare i - probabilmente minore - rettifiche di prendere il cv :: Mat direttamente, il layout di memoria è lo stesso, è "solo" l'intestazione che è diverso)

+0

il progetto che sto lavorando mi obbliga a stampare l'immagine sul QtGui, ma lavorerà sulle immagini cv :: Mat maggior parte del tempo. Il motivo che chiedo è che il mio programma farà un sacco di calcolo quindi voglio eliminare il sovraccarico di conversione cv :: Mat per IplImage e poi a QImage. Questo è il mio primo progetto di elaborazione delle immagini, quindi non so davvero molto circa i dati che sono in un'immagine. Lo scoprirò abbastanza presto una volta che mi tufferò più nel progetto. Ad ogni modo, se non riesco a trovare nulla, seguirò il tuo suggerimento =) – Hien

+0

Puoi condividere il codice per IplImage -> Conversione QImage? Mi interessa apportare le modifiche necessarie per convertirlo in cv :: Mat. –

+0

@Hien: Il sovraccarico della conversione cv :: Mat in IplImage è, rispetto a tutte le altre cose che si fanno durante l'elaborazione delle immagini, completamente trascurabile. Non c'è copia dei dati coinvolti. – etarion

21

per convertire cv::Mat-QImage, si potrebbe tentare di utilizzare il costruttore QImage(uchar * data, int width, int height, Format format) come segue (mat è una cv::Mat):.

QImage img((uchar*)mat.data, mat.cols, mat.rows, QImage::Format_RGB32); 

È più efficiente della conversione manuale dei pixel in t he QImage, ma è necessario mantenere l'immagine originale cv::Mat nella memoria. Può essere facilmente convertito in una QPixmap e visualizzato utilizzando un QLabel:

QPixmap pixmap = QPixmap::fromImage(img); 
myLabel.setPixmap(pixmap); 

Aggiornamento

Perché OpenCV utilizza ordine BGR per impostazione predefinita, è necessario prima utilizzare cvtColor(src, dst, CV_BGR2RGB) per ottenere un layout di immagine che Qt capisce.

Aggiornamento 2:

Se l'immagine che si sta tentando di mostrare ha non standard stride (quando è non continuo, sottomatrice), l'immagine potrebbe appeard distorta. In questo caso, è meglio specificare esplicitamente il passo con cv::Mat::step1():

QImage img((uchar*)mat.data, mat.cols, mat.rows, mat.step1(), QImage::Format_RGB32); 
+0

Questo non funzionerà nel caso generale. Cosa succede se mat ha> 1 canali o il formato dei dati non è RGB32? –

+2

Beh, sì, e può anche avere più dimensioni, o può avere diverse 'dimensioni del passo'. Mi aspetto che Hien voglia visualizzare 'standard' cv :: Mat, che è stato caricato da 'imread' o convertito in un tipo appropriato. –

+0

@ Michael Sì, è quello che volevo. Proverò il tuo codice una volta che avrò il tempo di lavorare al mio progetto. =) – Hien

1

This post mostra come convertire un QImage per IplImage di OpenCV e vise-versa.

Dopo di che, se avete bisogno dell'aiuto per la conversione tra IplImage*-cv::Mat:

// Assume data is stored by: 
// IplImage* image; 

cv::Mat mat(image, true); // Copies the data from image 

cv::Mat mat(image, false); // Doesn't copy the data! 

E 'un hack, ma otterrà il lavoro fatto.

28

Questo è il codice per la virgola mobile a 24 bit RGB e in scala di grigi. Facilmente regolabile per altri tipi. È efficiente come sembra.

QImage Mat2QImage(const cv::Mat3b &src) { 
     QImage dest(src.cols, src.rows, QImage::Format_ARGB32); 
     for (int y = 0; y < src.rows; ++y) { 
       const cv::Vec3b *srcrow = src[y]; 
       QRgb *destrow = (QRgb*)dest.scanLine(y); 
       for (int x = 0; x < src.cols; ++x) { 
         destrow[x] = qRgba(srcrow[x][2], srcrow[x][1], srcrow[x][0], 255); 
       } 
     } 
     return dest; 
} 


QImage Mat2QImage(const cv::Mat_<double> &src) 
{ 
     double scale = 255.0; 
     QImage dest(src.cols, src.rows, QImage::Format_ARGB32); 
     for (int y = 0; y < src.rows; ++y) { 
       const double *srcrow = src[y]; 
       QRgb *destrow = (QRgb*)dest.scanLine(y); 
       for (int x = 0; x < src.cols; ++x) { 
         unsigned int color = srcrow[x] * scale; 
         destrow[x] = qRgba(color, color, color, 255); 
       } 
     } 
     return dest; 
} 
+1

Questo ha risposto a una domanda che stavo per pubblicare sulla conversione dei valori in scala di grigio a virgola mobile in un QImage ... grazie! –

3
Mat opencv_image = imread("fruits.jpg", CV_LOAD_IMAGE_COLOR); 
    Mat dest; 
    cvtColor(opencv_image, dest,CV_BGR2RGB); 
    QImage image((uchar*)dest.data, dest.cols, dest.rows,QImage::Format_RGB888); 

Questo è ciò che ha funzionato per me. Ho modificato il codice di Michal Kottman qui sopra.

27

La risposta di Michal Kottman è valida e fornisce risultati previsti per alcune immagini, ma in alcuni casi non riuscirà. Ecco una soluzione che ho trovato a questo problema.

QImage imgIn= QImage((uchar*) img.data, img.cols, img.rows, img.step, QImage::Format_RGB888); 

La differenza è l'aggiunta della parte img.step. qt non si lamenterà senza di esso ma alcune immagini non verranno visualizzate correttamente senza di essa. Spero che questo ti sia d'aiuto.

+3

Questo è essenzialmente ciò che OpenCV utilizza internamente per convertire (http://code.opencv.org/projects/opencv/repository/revisions/df262b340cbe1b362977573bb34138d837f79648/entry/modules/highgui/src/window_QT.cpp, riga 2389) 'image2Draw_qt = QImage (image2Draw_mat-> data.ptr, image2Draw_mat-> cols, image2Draw_mat-> rows, image2Draw_mat-> step, QImage :: Format_RGB888); 'Also (about, line 2400)' cvConvertImage (mat, image2Draw_mat, CV_CVTIMG_SWAP_RB); 'per convertire da BGR a RGB. –

+0

'img.step' fa la differenza. Stavo avendo strani problemi con questa conversione, incluso avere l'immagine risultante distorta e con i colori sbagliati. Provando un po 'ho ottenuto lo stesso codice. – MeloMCR

+0

grazie! quello era esattamente quello che stavo cercando! – Dredok

1

utilizzare la funzione convert16uc1 statica per l'immagine di profondità:

QPixmap Viewer::convert16uc1(const cv::Mat& source) 
{ 
    quint16* pSource = (quint16*) source.data; 
    int pixelCounts = source.cols * source.rows; 

    QImage dest(source.cols, source.rows, QImage::Format_RGB32); 

    char* pDest = (char*) dest.bits(); 

    for (int i = 0; i < pixelCounts; i++) 
    { 
    quint8 value = (quint8) ((*(pSource)) >> 8); 
    *(pDest++) = value; // B 
    *(pDest++) = value; // G 
    *(pDest++) = value; // R 
    *(pDest++) = 0;  // Alpha 
    pSource++; 
    } 

    return QPixmap::fromImage(dest); 
} 

QPixmap Viewer::convert8uc3(const cv::Mat& source) 
{ 
    quint8* pSource = source.data; 
    int pixelCounts = source.cols * source.rows; 

    QImage dest(source.cols, source.rows, QImage::Format_RGB32); 

    char* pDest = (char*) dest.bits(); 

    for (int i = 0; i < pixelCounts; i++) 
    { 
    *(pDest++) = *(pSource+2); // B 
    *(pDest++) = *(pSource+1); // G 
    *(pDest++) = *(pSource+0); // R 
    *(pDest++) = 0;    // Alpha 
    pSource+=3; 
    } 

    return QPixmap::fromImage(dest); 
} 

QPixmap Viewer::convert16uc3(const cv::Mat& source) 
{ 
    quint16* pSource = (quint16*) source.data; 
    int pixelCounts = source.cols * source.rows; 

    QImage dest(source.cols, source.rows, QImage::Format_RGB32); 

    char* pDest = (char*) dest.bits(); 

    for (int i = 0; i < pixelCounts; i++) 
    { 
    *(pDest++) = *(pSource+2); // B 
    *(pDest++) = *(pSource+1); // G 
    *(pDest++) = *(pSource+0); // R 
    *(pDest++) = 0;    // Alpha 
    pSource+=3; 
    } 

    return QPixmap::fromImage(dest); 
} 
0

Questo ha fatto il trucco per me. E 'un po' rischiosa, ha terribile prestazioni (come evidenziato nei commenti), ma funziona con tutti i formati di colore ho lanciate finora, ed è anche molto semplice da fare.

La procedura è la seguente:

cv::Mat image = //...some image you want to display 

// 1. Save the cv::Mat to some temporary file 
cv::imwrite("../Images/tmp.jpg",image); 

// 2. Load the image you just saved as a QImage 
QImage img; 
img.load("../Images/tmp.jpg"); 

Fatto!

Se, ad esempio, si desidera visualizzare in un QLabel, per poi proseguire con:

// Set QImage as content of MyImageQLabel 
ui-> MyImageQLabel->setPixmap(QPixmap::fromImage(img, Qt::AutoColor)); 

Io personalmente uso questo per un semplice editor di immagini.

+2

Questo ha una performance terribile. – AxeEffect

+0

@AxeEffect Questa non è certamente una soluzione ad alte prestazioni, considerando che è necessario eseguire operazioni di lettura/scrittura verso l'HDD. Per alcuni usi, però, questo non ha molta importanza. :-) –

1

Ho lo stesso problema come voi troppo, così ho sviluppare quattro funzioni per alleviare il mio dolore, sono

QImage mat_to_qimage_cpy(cv::Mat const &mat, bool swap = true); 

QImage mat_to_qimage_ref(cv::Mat &mat, bool swap = true); 

cv::Mat qimage_to_mat_cpy(QImage const &img, bool swap = true); 

cv::Mat qimage_to_mat_ref(QImage &img, bool swap = true); 

Queste funzioni in grado di gestire le immagini con 1, 3, 4 canali, ogni pixel deve occupare solo un byte (CV_8U-> Format_Indexed8, CV_8UC3-> QImage :: Format_RGB888, CV_8UC4-> QImage :: Format_ARGB32), non mi occupo ancora di altri tipi (QImage :: Format_RGB16, QImage :: Format_RGB666 e così sopra). I codici si trovano a github.

I concetti chiave di ** trasformare tappetino per Qimage ** sono

/** 
* @brief copy QImage into cv::Mat 
*/ 
struct mat_to_qimage_cpy_policy 
{ 
    static QImage start(cv::Mat const &mat, QImage::Format format) 
    { 
     //The fourth parameters--mat.step is crucial, because 
     //opencv may do padding on every row, you need to tell 
     //the qimage how many bytes per row 
     //The last thing is if you want to copy the buffer of cv::Mat 
     //to the qimage, you need to call copy(), else the qimage 
     //will share the buffer of cv::Mat 
     return QImage(mat.data, mat.cols, mat.rows, mat.step, format).copy(); 
    } 
}; 

struct mat_to_qimage_ref_policy 
{ 
    static QImage start(cv::Mat &mat, QImage::Format format) 
    { 
     //every thing are same as copy policy, but this one share 
     //the buffer of cv::Mat but not copy 
     return QImage(mat.data, mat.cols, mat.rows, mat.step, format); 
    } 
}; 

I concetti chiave di trasformare cv::Mat to Qimage sono

/** 
* @brief copy QImage into cv::Mat 
*/ 
struct qimage_to_mat_cpy_policy 
{ 
    static cv::Mat start(QImage const &img, int format) 
    { 
     //same as convert mat to qimage, the fifth parameter bytesPerLine() 
     //indicate how many bytes per row 
     //If you want to copy the data you need to call clone(), else QImage 
     //cv::Mat will share the buffer 
     return cv::Mat(img.height(), img.width(), format, 
         const_cast<uchar*>(img.bits()), img.bytesPerLine()).clone(); 
    } 
}; 

/** 
* @brief make Qimage and cv::Mat share the same buffer, the resource 
* of the cv::Mat must not deleted before the QImage finish 
* the jobs. 
*/ 
struct qimage_to_mat_ref_policy 
{ 
    static cv::Mat start(QImage &img, int format) 
    { 
     //same as copy policy, but this one will share the buffer 
     return cv::Mat(img.height(), img.width(), format, 
         img.bits(), img.bytesPerLine()); 
    } 
}; 

Se sarebbe bene se qualcuno può estendere queste funzioni e fargli supportare più tipi, per favore informami se ci sono dei bug.

1

Potrebbe sembrare sciocco, ma salvare l'immagine in una cartella e quindi la lettura in un oggetto QImage mi sembrava il modo più rapido.

Mat x = imread("--.jpg"); 
imwrite("x.jpg", x); 
QImage img("x.jpg"); 
Problemi correlati