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
risposta
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)
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
Puoi condividere il codice per IplImage -> Conversione QImage? Mi interessa apportare le modifiche necessarie per convertirlo in cv :: Mat. –
@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
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);
Questo non funzionerà nel caso generale. Cosa succede se mat ha> 1 canali o il formato dei dati non è RGB32? –
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. –
@ Michael Sì, è quello che volevo. Proverò il tuo codice una volta che avrò il tempo di lavorare al mio progetto. =) – Hien
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.
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;
}
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! –
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.
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.
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. –
'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
grazie! quello era esattamente quello che stavo cercando! – Dredok
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);
}
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.
Questo ha una performance terribile. – AxeEffect
@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. :-) –
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.
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");
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 ... –