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