File indexing completed on 2025-01-19 03:57:40

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2013-06-16
0007  * Description : Functions to convert between OpenCV's cv::Mat and Qt's QImage and QPixmap.
0008  *               Partially inspired from:
0009  *               https://asmaloney.com/2013/11/code/converting-between-cvmat-and-qimage-or-qpixmap
0010  *
0011  * SPDX-FileCopyrightText:      2013 by Andy Maloney <asmaloney at gmail dot com>
0012  * SPDX-FileCopyrightText: 2015-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0013  *
0014  * SPDX-License-Identifier: GPL-2.0-or-later
0015  *
0016  * ============================================================ */
0017 
0018 #ifndef DIGIKAM_QT_OPENCV_H
0019 #define DIGIKAM_QT_OPENCV_H
0020 
0021 // Qt includes
0022 
0023 #include <QImage>
0024 #include <QPixmap>
0025 
0026 // Local includes
0027 
0028 #include "digikam_opencv.h"
0029 #include "digikam_debug.h"
0030 
0031 namespace QtOpenCV
0032 {
0033     // NOTE: This does not cover all cases - it should be easy to add new ones as required.
0034 
0035     inline QImage cvMatToQImage(const cv::Mat& inMat)
0036     {
0037         switch (inMat.type())
0038         {
0039             // 8-bit, 4 channel
0040 
0041             case CV_8UC4:
0042             {
0043                 QImage image(inMat.data,
0044                              inMat.cols,
0045                              inMat.rows,
0046                              static_cast<int>(inMat.step),
0047                              QImage::Format_ARGB32);
0048 
0049                 return image;
0050             }
0051 
0052             // 8-bit, 3 channel
0053 
0054             case CV_8UC3:
0055             {
0056                 QImage image(inMat.data,
0057                              inMat.cols,
0058                              inMat.rows,
0059                              static_cast<int>(inMat.step),
0060                              QImage::Format_RGB888);
0061 
0062                 return image.rgbSwapped();
0063             }
0064 
0065             // 8-bit, 1 channel
0066 
0067             case CV_8UC1:
0068             {
0069                 QImage image(inMat.data,
0070                           inMat.cols,
0071                           inMat.rows,
0072                           static_cast<int>(inMat.step),
0073                           QImage::Format_Grayscale8);
0074 
0075                 return image;
0076             }
0077 
0078             default:
0079             {
0080                 qCWarning(DIGIKAM_TESTS_LOG) << "cvMatToQImage() - cv::Mat image type not handled in switch:" << inMat.type();
0081                 break;
0082             }
0083         }
0084 
0085         return QImage();
0086     }
0087 
0088     // ----------------------------------------------------------------------------------
0089 
0090     inline QPixmap cvMatToQPixmap(const cv::Mat& inMat)
0091     {
0092         return QPixmap::fromImage(cvMatToQImage(inMat));
0093     }
0094 
0095     // ----------------------------------------------------------------------------------
0096 
0097     /**
0098      * If inImage exists for the lifetime of the resulting cv::Mat, pass false to inCloneImageData to share inImage's
0099      * data with the cv::Mat directly
0100      * NOTE: Format_RGB888 is an exception since we need to use a local QImage and thus must clone the data regardless
0101      * NOTE: This does not cover all cases - it should be easy to add new ones as required.
0102      */
0103     inline cv::Mat QImageToCvMat(const QImage& inImage, bool inCloneImageData = true)
0104     {
0105         switch (inImage.format())
0106         {
0107             // 8-bit, 4 channel
0108 
0109             case QImage::Format_ARGB32:
0110             case QImage::Format_ARGB32_Premultiplied:
0111             {
0112                 cv::Mat mat(inImage.height(),
0113                             inImage.width(),
0114                             CV_8UC4,
0115                             const_cast<uchar*>(inImage.bits()),
0116                             static_cast<size_t>(inImage.bytesPerLine()));
0117 
0118                 return (inCloneImageData ? mat.clone() : mat);
0119             }
0120 
0121             // 8-bit, 3 channel
0122 
0123             case QImage::Format_RGB32:
0124             case QImage::Format_RGB888:
0125             {
0126                 if (!inCloneImageData)
0127                 {
0128                     qCWarning(DIGIKAM_TESTS_LOG) << "QImageToCvMat() - Conversion requires cloning because we use a temporary QImage";
0129                 }
0130 
0131                 QImage swapped;
0132 
0133                 if (inImage.format() == QImage::Format_RGB32)
0134                 {
0135                     swapped = inImage.convertToFormat(QImage::Format_RGB888);
0136                 }
0137                 else
0138                 {
0139                     swapped = inImage.rgbSwapped();
0140                 }
0141 
0142                 return cv::Mat(swapped.height(),
0143                                swapped.width(),
0144                                CV_8UC3,
0145                                const_cast<uchar*>(swapped.bits()),
0146                                static_cast<size_t>(swapped.bytesPerLine())
0147                               ).clone();
0148             }
0149 
0150             // 8-bit, 1 channel
0151 
0152             case QImage::Format_Indexed8:
0153             {
0154                 cv::Mat mat(inImage.height(),
0155                             inImage.width(),
0156                             CV_8UC1,
0157                             const_cast<uchar*>(inImage.bits()),
0158                             static_cast<size_t>(inImage.bytesPerLine()));
0159 
0160                 return (inCloneImageData ? mat.clone() : mat);
0161             }
0162 
0163             default:
0164             {
0165                 qCWarning(DIGIKAM_TESTS_LOG) << "QImageToCvMat() - QImage format not handled in switch:" << inImage.format();
0166                 break;
0167             }
0168         }
0169 
0170         return cv::Mat();
0171     }
0172 
0173 
0174     // ----------------------------------------------------------------------------------
0175 
0176     /**
0177      * If inPixmap exists for the lifetime of the resulting cv::Mat, pass false to inCloneImageData to share inPixmap's data
0178      * with the cv::Mat directly
0179      * NOTE: Format_RGB888 is an exception since we need to use a local QImage and thus must clone the data regardless
0180      */
0181     inline cv::Mat QPixmapToCvMat(const QPixmap& inPixmap, bool inCloneImageData = true)
0182     {
0183         return QImageToCvMat(inPixmap.toImage(), inCloneImageData);
0184     }
0185 
0186 } // namespace QtOpenCV
0187 
0188 #endif // DIGIKAM_QT_OPENCV_H