web-dev-qa-db-fra.com

comment convertir un opencv cv :: Mat en qimage

Je me demande comment convertir le type cv :: Mat standard OpenCV C++ en Qimage. J'ai cherché partout, mais je n'ai pas eu de chance. J'ai trouvé du code qui convertit l'IPlimage en Qimage, mais ce n'est pas ce que je veux. Merci

33
Hien

La réponse de Michal Kottman est valide et donne le résultat attendu pour certaines images, mais elle échouera dans certains cas. Voici une solution que j'ai trouvée à ce problème.

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

La différence consiste à ajouter une partie img.step. qt ne se plaindra pas sans elle mais certaines images ne s'afficheront pas correctement sans elle. J'espère que cela aidera.

34
chAmi

Voici le code pour la virgule flottante 24 bits RVB et en niveaux de gris. Facilement réglable pour les autres types. C'est aussi efficace que possible.

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;
}
29
ypnos

Pour convertir de cv::Mat à QImage, vous pouvez utiliser le constructeur QImage(uchar * data, int width, int height, Format format) comme suit (mat est un cv::Mat):

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

Il est plus efficace que de convertir manuellement les pixels en QImage, mais vous devez conserver l’image cv::Mat originale en mémoire. Il peut être facilement converti en QPixmap et affiché à l'aide de QLabel:

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

Mettre à jour

Etant donné qu'OpenCV utilise l'ordre BGR par défaut, vous devez d'abord utiliser cvtColor(src, dst, CV_BGR2RGB) pour obtenir une présentation d'image que Qt comprend.

Mise à jour 2:

Si l'image que vous essayez d'afficher a une valeur non standard stride (lorsqu'elle est non continue, une sous-matrice), elle peut apparaître déformée. Dans ce cas, il est préférable de spécifier explicitement le pas en utilisant cv::Mat::step1():

QImage img((uchar*)mat.data, mat.cols, mat.rows, mat.step1(), QImage::Format_RGB32);
23
Michal Kottman
    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);

C'est ce qui a fonctionné pour moi. J'ai modifié le code de Michal Kottman ci-dessus.

3
Mathai

OpenCV charge les images dans une Mat au format Bleu-Vert-Rouge (BGR) par défaut, alors que QImage attend RGB. Cela signifie que si vous convertissez une Mat en QImage, les canaux bleu et rouge seront échangés. Pour résoudre ce problème, avant de construire la QImage, vous devez changer le format BRG de votre Mat en RVB, via la méthode cvtColor à l'aide de l'argument CV_BGR2RGB, comme suit:

Mat mat = imread("path/to/image.jpg");
cvtColor(mat, mat, CV_BGR2RGB);
QImage image(mat.data, mat.cols, mat.rows, QImage::Format_RGB888);

Sinon, utilisez rgbSwapped() sur la QImage

QImage image = QImage(mat.data, mat.cols, mat.rows, QImage::Format_RGB888).rgbSwapped());
2
Gunnar Karlsson

cv :: Mat a un opérateur de conversion en IplImage, donc si vous avez quelque chose qui convertit l'IplImage en QImage, utilisez-le (ou effectuez les ajustements - probablement mineurs - pour prendre cv :: Mat directement, la disposition de la mémoire est la même chose, c’est "juste" l’en-tête qui est différent.)

2
etarion

J'ai le même problème que vous aussi, alors je développe quatre fonctions pour soulager ma douleur, elles sont 

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

Ces fonctions peuvent gérer les images avec 1, 3, 4 canaux, chaque pixel doit occuper un octet seulement (CV_8U-> Format_Indexed8, CV_8UC3-> QImage :: Format_RGB888, CV_8UC4-> QImage :: Format_ARGB32 ), Je ne traite pas encore d’autres types (QImage :: Format_RGB16, QImage :: Format_RGB666, etc.). Les codes sont situésat github .

Les concepts clés de ** transformer mat en Qimage ** sont

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

Les concepts clé de transform cv::Mat to Qimage sont

/**
 * @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 ce serait bien si quelqu'un pouvait étendre ces fonctions et les obliger à supporter plus de types, merci de me prévenir s'il y a des bugs.

2
StereoMatching

Cet article montre comment convertir une QImage en OpenCV's IplImage et inversement.

Après cela, si vous avez besoin d’aide pour convertir entre IplImage* et 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!

C'est un bidouillage, mais fera le travail.

1
karlphillip

Utilisez la fonction statique convert16uc1 pour l'image de profondeur:

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);
}
1
D. Sangue

Cela peut sembler idiot, mais enregistrer l'image dans un dossier puis le lire dans un objet QImage m'a semblé le moyen le plus rapide.

Mat x = imread("--.jpg");
imwrite("x.jpg", x);
QImage img("x.jpg");
0
Burak Mete