2011-02-17 54 views
31

Me pregunto cómo convertiría el tipo de cv :: Mat de OpenCV C++ a Qimage. He estado buscando, pero no tengo suerte. He encontrado un código que convierte el IPlimage en Qimage, pero eso no es lo que quiero. Graciascómo convertir un cc de opencv :: Mat a qimage

+0

Será útil si existe dicho código, pero hay algunas cuestiones por escrito que: el apoyo cv :: Mat más tipos de datos que QImage, cv :: Mat es compatible con múltiples canales, se debe copiar o envuelve los datos alrededor de los datos originales ... –

Respuesta

2

cv :: Mat tiene un operador de conversión a IplImage, por lo que si usted tiene algo que convierte la IplImage a un QImage, sólo tiene que utilizar (o hacer que los - probablemente menores - ajustes para tener en el cv :: Mat directamente, el diseño de memoria es la misma, es "sólo" la cabecera que es diferente)

+0

el proyecto que estoy trabajando en mi requiere para imprimir la imagen en el QtGui, pero voy a estar trabajando en las imágenes en el cv :: Mat mayor parte del tiempo. La razón por la que estoy preguntando es que mi programa hará una gran cantidad de cálculos, así que quiero deshacerme de la sobrecarga de convertir cv :: Mat a IplImage y luego a qimage. Este es mi primer proyecto de procesamiento de imágenes, así que no sé mucho sobre los datos que están en una imagen. Aprenderé sobre esto lo suficientemente pronto una vez que me sumerja más en el proyecto. De todos modos, si no puedo encontrar nada, seguiré tu sugerencia =) – Hien

+0

¿Puedes compartir el código de IplImage -> QImage conversion? Estoy interesado en hacer los ajustes necesarios para convertirlo a cv :: Mat. –

+0

@Hien: La sobrecarga de la conversión cv :: Mat a IplImage es, en comparación con todas las otras cosas que hacer cuando se hace el procesamiento de imágenes, completamente despreciable. No hay copia de datos involucrados. – etarion

21

convertir de cv::Mat a QImage, usted podría tratar de utilizar el constructor QImage(uchar * data, int width, int height, Format format) de la siguiente manera (mat es una cv::Mat):.

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

Es más eficiente que convertir manualmente los píxeles a t él QImage, pero debe conservar la imagen original cv::Mat en la memoria. Se puede convertir fácilmente a un QPixmap y se muestra el uso de un QLabel:

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

actualización

Debido OpenCV utiliza para BGR por defecto, primero debe utilizar cvtColor(src, dst, CV_BGR2RGB) para obtener un diseño de imagen que Qt entiende.

Actualización 2:

Si la imagen que está tratando de mostrar no estándar tiene stride (cuando es no continua, submatriz), la imagen distorsionada puede appeard. En este caso, es mejor especificar explícitamente el uso de zancada cv::Mat::step1():

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

Esto no funcionará en el caso general. ¿Qué pasa si mat tiene más de 1 canal o el formato de datos no es RGB32? –

+2

Bueno, sí, y también puede tener múltiples dimensiones, o puede tener diferente 'tamaño de paso'. Estoy esperando que Hien quiera mostrar cv :: Mat 'estándar', es decir, cargado por 'imread', o convertido al tipo apropiado. –

+0

@Michael Sí, eso es lo que quería. Probaré tu código una vez que tenga tiempo para trabajar en mi proyecto. =) – Hien

1

This post muestra cómo convertir un QImage a IplImage de OpenCV y viceversa.

Después de eso, si necesita ayuda para convertir entre IplImage* a 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! 

Es un truco, pero conseguirá el trabajo hecho.

28

Aquí hay un código para el punto flotante de 24 bits RGB y en escala de grises. Fácilmente ajustable para otros tipos. Es lo más eficiente posible.

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

Respondió una pregunta que estaba a punto de publicar sobre la conversión de valores de escala de grises en coma flotante en una QImage ... ¡gracias! –

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

Esto es lo que funcionó para mí. Modifiqué el código de Michal Kottman arriba.

27

La respuesta de Michal Kottman es válida y da el resultado esperado para algunas imágenes, pero fallará en algunos casos. Aquí hay una solución que encontré para ese problema.

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

Diferencia está agregando parte img.step. qt no se quejará sin el pero algunas imágenes no se mostrarán correctamente sin él. Espero que esto ayude

+3

Esto es esencialmente lo que OpenCV usa internamente para convertir (http://code.opencv.org/projects/opencv/repository/revisions/df262b340cbe1b362977573bb34138d837f79648/entry/modules/highgui/src/window_QT.cpp, línea 2389) 'image2Draw_qt = QImage (image2Draw_mat-> data.ptr, image2Draw_mat-> cols, image2Draw_mat-> rows, image2Draw_mat-> step, QImage :: Format_RGB888); 'También (aproximadamente, línea 2400)' cvConvertImage (mat, image2Draw_mat, CV_CVTIMG_SWAP_RB); 'para convertir de BGR a RGB. –

+0

'img.step' marca la diferencia. Estaba teniendo problemas extraños con esta conversión, incluida la visualización de la imagen resultante distorsionada y con los colores incorrectos. Probando un poco, obtuve el mismo código. – MeloMCR

+0

gracias! ¡eso era exactamente lo que estaba buscando! – Dredok

1

Utilice la función estática convert16uc1 la imagen de profundidad para:

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

Esto hizo el truco para mí. Es un poco dudoso, tiene un rendimiento terrible (como se señala en los comentarios), pero funciona con todos los formatos de color que he lanzado hasta el momento, y también es muy simple de hacer.

El procedimiento es el siguiente:

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

hecho!

Si, por ejemplo, desea que se muestre en un QLabel, y luego continuar con:

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

Yo personalmente lo uso para un simple editor de imágenes.

+2

Esto tiene un rendimiento terrible. – AxeEffect

+0

@AxeEffect Esto ciertamente no es una solución de alto rendimiento, teniendo en cuenta que tiene que hacer una operación de lectura/escritura hacia el HDD. Para algunos usos, sin embargo, eso realmente no importa. :-) –

1

que tienen el mismo problema que tú también, así que desarrolla cuatro funciones para aliviar mi dolor, que son

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

Estas funciones pueden manejar las imágenes con 1, 3, 4 canales, cada píxel debe ocupar un byte solamente (CV_8U-> Format_Indexed8, CV_8UC3-> QImage :: Format_RGB888, CV_8UC4-> QImage :: Format_ARGB32), aún no me ocupo de otros tipos (QImage :: Format_RGB16, QImage :: Format_RGB666 y así en). Los códigos están ubicados en github.

Los conceptos clave de transformar ** ** estera a Qimage son

/** 
* @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); 
    } 
}; 

Los conceptos clave de transformar cv::Mat to Qimage son

/** 
* @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()); 
    } 
}; 

Si sería bueno si alguien puede amplíe estas funciones y haga que admitan más tipos, infórmeme si hay algún error.

1

Puede parecer tonto, pero guardar la imagen en una carpeta y luego leer en un objeto QImage me parecía la forma más rápida.

Mat x = imread("--.jpg"); 
imwrite("x.jpg", x); 
QImage img("x.jpg"); 
Cuestiones relacionadas