1 /* ============================================================ 2 * 3 * This file is a part of digiKam project 4 * https://www.digikam.org 5 * 6 * Date : 2013-06-16 7 * Description : Functions to convert between OpenCV's cv::Mat and Qt's QImage and QPixmap. 8 * Partially inspired from: 9 * https://asmaloney.com/2013/11/code/converting-between-cvmat-and-qimage-or-qpixmap 10 * 11 * Copyright (C) 2013 by Andy Maloney <asmaloney at gmail dot com> 12 * Copyright (C) 2015-2021 by Gilles Caulier <caulier dot gilles at gmail dot com> 13 * 14 * This program is free software; you can redistribute it 15 * and/or modify it under the terms of the GNU General 16 * Public License as published by the Free Software Foundation; 17 * either version 2, or (at your option) 18 * any later version. 19 * 20 * This program is distributed in the hope that it will be useful, 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * GNU General Public License for more details. 24 * 25 * ============================================================ */ 26 27 #ifndef DIGIKAM_QT_OPENCV_H 28 #define DIGIKAM_QT_OPENCV_H 29 30 // Qt includes 31 32 #include <QImage> 33 #include <QPixmap> 34 35 // Local includes 36 37 #include "digikam_opencv.h" 38 #include "digikam_debug.h" 39 40 namespace QtOpenCV 41 { 42 // NOTE: This does not cover all cases - it should be easy to add new ones as required. 43 cvMatToQImage(const cv::Mat & inMat)44 inline QImage cvMatToQImage(const cv::Mat& inMat) 45 { 46 switch (inMat.type()) 47 { 48 // 8-bit, 4 channel 49 50 case CV_8UC4: 51 { 52 QImage image(inMat.data, 53 inMat.cols, 54 inMat.rows, 55 static_cast<int>(inMat.step), 56 QImage::Format_ARGB32); 57 58 return image; 59 } 60 61 // 8-bit, 3 channel 62 63 case CV_8UC3: 64 { 65 QImage image(inMat.data, 66 inMat.cols, 67 inMat.rows, 68 static_cast<int>(inMat.step), 69 QImage::Format_RGB888); 70 71 return image.rgbSwapped(); 72 } 73 74 // 8-bit, 1 channel 75 76 case CV_8UC1: 77 { 78 QImage image(inMat.data, 79 inMat.cols, 80 inMat.rows, 81 static_cast<int>(inMat.step), 82 QImage::Format_Grayscale8); 83 84 return image; 85 } 86 87 default: 88 { 89 qCWarning(DIGIKAM_TESTS_LOG) << "cvMatToQImage() - cv::Mat image type not handled in switch:" << inMat.type(); 90 break; 91 } 92 } 93 94 return QImage(); 95 } 96 97 // ---------------------------------------------------------------------------------- 98 cvMatToQPixmap(const cv::Mat & inMat)99 inline QPixmap cvMatToQPixmap(const cv::Mat& inMat) 100 { 101 return QPixmap::fromImage(cvMatToQImage(inMat)); 102 } 103 104 // ---------------------------------------------------------------------------------- 105 106 /** 107 * If inImage exists for the lifetime of the resulting cv::Mat, pass false to inCloneImageData to share inImage's 108 * data with the cv::Mat directly 109 * NOTE: Format_RGB888 is an exception since we need to use a local QImage and thus must clone the data regardless 110 * NOTE: This does not cover all cases - it should be easy to add new ones as required. 111 */ 112 inline cv::Mat QImageToCvMat(const QImage& inImage, bool inCloneImageData = true) 113 { 114 switch (inImage.format()) 115 { 116 // 8-bit, 4 channel 117 118 case QImage::Format_ARGB32: 119 case QImage::Format_ARGB32_Premultiplied: 120 { 121 cv::Mat mat(inImage.height(), 122 inImage.width(), 123 CV_8UC4, 124 const_cast<uchar*>(inImage.bits()), 125 static_cast<size_t>(inImage.bytesPerLine())); 126 127 return (inCloneImageData ? mat.clone() : mat); 128 } 129 130 // 8-bit, 3 channel 131 132 case QImage::Format_RGB32: 133 case QImage::Format_RGB888: 134 { 135 if (!inCloneImageData) 136 { 137 qCWarning(DIGIKAM_TESTS_LOG) << "QImageToCvMat() - Conversion requires cloning because we use a temporary QImage"; 138 } 139 140 QImage swapped; 141 142 if (inImage.format() == QImage::Format_RGB32) 143 { 144 swapped = inImage.convertToFormat(QImage::Format_RGB888); 145 } 146 else 147 { 148 swapped = inImage.rgbSwapped(); 149 } 150 151 return cv::Mat(swapped.height(), 152 swapped.width(), 153 CV_8UC3, 154 const_cast<uchar*>(swapped.bits()), 155 static_cast<size_t>(swapped.bytesPerLine()) 156 ).clone(); 157 } 158 159 // 8-bit, 1 channel 160 161 case QImage::Format_Indexed8: 162 { 163 cv::Mat mat(inImage.height(), 164 inImage.width(), 165 CV_8UC1, 166 const_cast<uchar*>(inImage.bits()), 167 static_cast<size_t>(inImage.bytesPerLine())); 168 169 return (inCloneImageData ? mat.clone() : mat); 170 } 171 172 default: 173 { 174 qCWarning(DIGIKAM_TESTS_LOG) << "QImageToCvMat() - QImage format not handled in switch:" << inImage.format(); 175 break; 176 } 177 } 178 179 return cv::Mat(); 180 } 181 182 183 // ---------------------------------------------------------------------------------- 184 185 /** 186 * If inPixmap exists for the lifetime of the resulting cv::Mat, pass false to inCloneImageData to share inPixmap's data 187 * with the cv::Mat directly 188 * NOTE: Format_RGB888 is an exception since we need to use a local QImage and thus must clone the data regardless 189 */ 190 inline cv::Mat QPixmapToCvMat(const QPixmap& inPixmap, bool inCloneImageData = true) 191 { 192 return QImageToCvMat(inPixmap.toImage(), inCloneImageData); 193 } 194 195 } // namespace QtOpenCV 196 197 #endif // DIGIKAM_QT_OPENCV_H 198