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