MauiKit Image Tools

cvmatandqimage.cpp
1/****************************************************************************
2** Copyright (c) 2012-2015 Debao Zhang <hello@debao.me>
3** All right reserved.
4**
5** Permission is hereby granted, free of charge, to any person obtaining
6** a copy of this software and associated documentation files (the
7** "Software"), to deal in the Software without restriction, including
8** without limitation the rights to use, copy, modify, merge, publish,
9** distribute, sublicense, and/or sell copies of the Software, and to
10** permit persons to whom the Software is furnished to do so, subject to
11** the following conditions:
12**
13** The above copyright notice and this permission notice shall be
14** included in all copies or substantial portions of the Software.
15**
16** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23**
24****************************************************************************/
25
26#include "cvmatandqimage.h"
27#include <QImage>
28#include <QDebug>
29#include <cstring>
30#include "opencv2/core/core.hpp"
31#include "opencv2/imgproc/imgproc.hpp"
32
33namespace QtOcv {
34namespace {
35
36/*ARGB <==> BGRA
37 */
38cv::Mat argb2bgra(const cv::Mat &mat)
39{
40 Q_ASSERT(mat.channels()==4);
41
42 cv::Mat newMat(mat.rows, mat.cols, mat.type());
43 int from_to[] = {0,3, 1,2, 2,1, 3,0};
44 cv::mixChannels(&mat, 1, &newMat, 1, from_to, 4);
45 return newMat;
46}
47
48cv::Mat adjustChannelsOrder(const cv::Mat &srcMat, MatColorOrder srcOrder, MatColorOrder targetOrder)
49{
50 Q_ASSERT(srcMat.channels()==4);
51
52 if (srcOrder == targetOrder)
53 return srcMat.clone();
54
55 cv::Mat desMat;
56
57 if ((srcOrder == MCO_ARGB && targetOrder == MCO_BGRA)
58 ||(srcOrder == MCO_BGRA && targetOrder == MCO_ARGB)) {
59 //ARGB <==> BGRA
60 desMat = argb2bgra(srcMat);
61 } else if (srcOrder == MCO_ARGB && targetOrder == MCO_RGBA) {
62 //ARGB ==> RGBA
63 desMat = cv::Mat(srcMat.rows, srcMat.cols, srcMat.type());
64 int from_to[] = {0,3, 1,0, 2,1, 3,2};
65 cv::mixChannels(&srcMat, 1, &desMat, 1, from_to, 4);
66 } else if (srcOrder == MCO_RGBA && targetOrder == MCO_ARGB) {
67 //RGBA ==> ARGB
68 desMat = cv::Mat(srcMat.rows, srcMat.cols, srcMat.type());
69 int from_to[] = {0,1, 1,2, 2,3, 3,0};
70 cv::mixChannels(&srcMat, 1, &desMat, 1, from_to, 4);
71 } else {
72 //BGRA <==> RBGA
73 cv::cvtColor(srcMat, desMat, cv::COLOR_BGRA2RGBA);
74 }
75 return desMat;
76}
77
78QImage::Format findClosestFormat(QImage::Format formatHint)
79{
80 QImage::Format format;
81 switch (formatHint) {
86#if QT_VERSION >= 0x040400
88#endif
89#if QT_VERSION >= 0x050200
93#endif
94#if QT_VERSION >= 0x050500
97#endif
98 format = formatHint;
99 break;
103 break;
105 format = QImage::Format_RGB32;
106 break;
107#if QT_VERSION > 0x040400
111 format = QImage::Format_RGB888;
112 break;
118 break;
119#endif
120 default:
121 format = QImage::Format_ARGB32;
122 break;
123 }
124 return format;
125}
126
127MatColorOrder getColorOrderOfRGB32Format()
128{
129#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
130 return MCO_BGRA;
131#else
132 return MCO_ARGB;
133#endif
134}
135} //namespace
136
137
138/* Convert QImage to cv::Mat
139 */
140cv::Mat image2Mat(const QImage &img, int requiredMatType, MatColorOrder requriedOrder)
141{
142 int targetDepth = CV_MAT_DEPTH(requiredMatType);
143 int targetChannels = CV_MAT_CN(requiredMatType);
144 Q_ASSERT(targetChannels==CV_CN_MAX || targetChannels==1 || targetChannels==3 || targetChannels==4);
145 Q_ASSERT(targetDepth==CV_8U || targetDepth==CV_16U || targetDepth==CV_32F);
146
147 if (img.isNull())
148 return cv::Mat();
149
150 //Find the closest image format that can be used in image2Mat_shared()
151 QImage::Format format = findClosestFormat(img.format());
152 QImage image = (format==img.format()) ? img : img.convertToFormat(format);
153
154 MatColorOrder srcOrder;
155 cv::Mat mat0 = image2Mat_shared(image, &srcOrder);
156
157 //Adjust mat channells if needed.
158 cv::Mat mat_adjustCn;
159 const float maxAlpha = targetDepth==CV_8U ? 255 : (targetDepth==CV_16U ? 65535 : 1.0);
160 if (targetChannels == CV_CN_MAX)
161 targetChannels = mat0.channels();
162 switch(targetChannels) {
163 case 1:
164 if (mat0.channels() == 3) {
165 cv::cvtColor(mat0, mat_adjustCn, cv::COLOR_RGB2GRAY);
166 } else if (mat0.channels() == 4) {
167 if (srcOrder == MCO_BGRA)
168 cv::cvtColor(mat0, mat_adjustCn, cv::COLOR_BGRA2GRAY);
169 else if (srcOrder == MCO_RGBA)
170 cv::cvtColor(mat0, mat_adjustCn, cv::COLOR_RGBA2GRAY);
171 else//MCO_ARGB
172 cv::cvtColor(argb2bgra(mat0), mat_adjustCn, cv::COLOR_BGRA2GRAY);
173 }
174 break;
175 case 3:
176 if (mat0.channels() == 1) {
177 cv::cvtColor(mat0, mat_adjustCn, requriedOrder == MCO_BGR ? cv::COLOR_GRAY2BGR : cv::COLOR_GRAY2RGB);
178 } else if (mat0.channels() == 3) {
179 if (requriedOrder != srcOrder)
180 cv::cvtColor(mat0, mat_adjustCn, cv::COLOR_RGB2BGR);
181 } else if (mat0.channels() == 4) {
182 if (srcOrder == MCO_ARGB) {
183 mat_adjustCn = cv::Mat(mat0.rows, mat0.cols, CV_MAKE_TYPE(mat0.type(), 3));
184 int ARGB2RGB[] = {1,0, 2,1, 3,2};
185 int ARGB2BGR[] = {1,2, 2,1, 3,0};
186 cv::mixChannels(&mat0, 1, &mat_adjustCn, 1, requriedOrder == MCO_BGR ? ARGB2BGR : ARGB2RGB, 3);
187 } else if (srcOrder == MCO_BGRA) {
188 cv::cvtColor(mat0, mat_adjustCn, requriedOrder == MCO_BGR ? cv::COLOR_BGRA2BGR : cv::COLOR_BGRA2RGB);
189 } else {//RGBA
190 cv::cvtColor(mat0, mat_adjustCn, requriedOrder == MCO_BGR ? cv::COLOR_RGBA2BGR : cv::COLOR_RGBA2RGB);
191 }
192 }
193 break;
194 case 4:
195 if (mat0.channels() == 1) {
196 if (requriedOrder == MCO_ARGB) {
197 cv::Mat alphaMat(mat0.rows, mat0.cols, CV_MAKE_TYPE(mat0.type(), 1), cv::Scalar(maxAlpha));
198 mat_adjustCn = cv::Mat(mat0.rows, mat0.cols, CV_MAKE_TYPE(mat0.type(), 4));
199 cv::Mat in[] = {alphaMat, mat0};
200 int from_to[] = {0,0, 1,1, 1,2, 1,3};
201 cv::mixChannels(in, 2, &mat_adjustCn, 1, from_to, 4);
202 } else if (requriedOrder == MCO_RGBA) {
203 cv::cvtColor(mat0, mat_adjustCn, cv::COLOR_GRAY2RGBA);
204 } else {//MCO_BGRA
205 cv::cvtColor(mat0, mat_adjustCn, cv::COLOR_GRAY2BGRA);
206 }
207 } else if (mat0.channels() == 3) {
208 if (requriedOrder == MCO_ARGB) {
209 cv::Mat alphaMat(mat0.rows, mat0.cols, CV_MAKE_TYPE(mat0.type(), 1), cv::Scalar(maxAlpha));
210 mat_adjustCn = cv::Mat(mat0.rows, mat0.cols, CV_MAKE_TYPE(mat0.type(), 4));
211 cv::Mat in[] = {alphaMat, mat0};
212 int from_to[] = {0,0, 1,1, 2,2, 3,3};
213 cv::mixChannels(in, 2, &mat_adjustCn, 1, from_to, 4);
214 } else if (requriedOrder == MCO_RGBA) {
215 cv::cvtColor(mat0, mat_adjustCn, cv::COLOR_RGB2RGBA);
216 } else {//MCO_BGRA
217 cv::cvtColor(mat0, mat_adjustCn, cv::COLOR_RGB2BGRA);
218 }
219 } else if (mat0.channels() == 4) {
220 if (srcOrder != requriedOrder)
221 mat_adjustCn = adjustChannelsOrder(mat0, srcOrder, requriedOrder);
222 }
223 break;
224 default:
225 break;
226 }
227
228 //Adjust depth if needed.
229 if (targetDepth == CV_8U)
230 return mat_adjustCn.empty() ? mat0.clone() : mat_adjustCn;
231
232 if (mat_adjustCn.empty())
233 mat_adjustCn = mat0;
234 cv::Mat mat_adjustDepth;
235 mat_adjustCn.convertTo(mat_adjustDepth, CV_MAKE_TYPE(targetDepth, mat_adjustCn.channels()), targetDepth == CV_16U ? 255.0 : 1/255.0);
236 return mat_adjustDepth;
237}
238
239/* Convert cv::Mat to QImage
240 */
241QImage mat2Image(const cv::Mat &mat, MatColorOrder order, QImage::Format formatHint)
242{
243 Q_ASSERT(mat.channels()==1 || mat.channels()==3 || mat.channels()==4);
244 Q_ASSERT(mat.depth()==CV_8U || mat.depth()==CV_16U || mat.depth()==CV_32F);
245
246 if (mat.empty())
247 return QImage();
248
249 //Adjust mat channels if needed, and find proper QImage format.
250 QImage::Format format;
251 cv::Mat mat_adjustCn;
252 if (mat.channels() == 1) {
253 format = formatHint;
254 if (formatHint != QImage::Format_Indexed8
255 #if QT_VERSION >= 0x050500
256 && formatHint != QImage::Format_Alpha8
257 && formatHint != QImage::Format_Grayscale8
258 #endif
259 ) {
261 }
262 } else if (mat.channels() == 3) {
263#if QT_VERSION >= 0x040400
264 format = QImage::Format_RGB888;
265 if (order == MCO_BGR)
266 cv::cvtColor(mat, mat_adjustCn, cv::COLOR_BGR2RGB);
267#else
268 format = QImage::Format_RGB32;
269 cv::Mat mat_tmp;
270 cv::cvtColor(mat, mat_tmp, order == MCO_BGR ? CV_BGR2BGRA : CV_RGB2BGRA);
271#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
272 mat_adjustCn = mat_tmp;
273#else
274 mat_adjustCn = argb2bgra(mat_tmp);
275#endif
276
277#endif
278 } else if (mat.channels() == 4) {
279 //Find best format if the formatHint can not be applied.
280 format = findClosestFormat(formatHint);
281 if (format != QImage::Format_RGB32
282 && format != QImage::Format_ARGB32
284 #if QT_VERSION >= 0x050200
285 && format != QImage::Format_RGBX8888
286 && format != QImage::Format_RGBA8888
288 #endif
289 ) {
290#if QT_VERSION >= 0x050200
291 format = order == MCO_RGBA ? QImage::Format_RGBA8888 : QImage::Format_ARGB32;
292#else
293 format = QImage::Format_ARGB32;
294#endif
295 }
296
297 //Channel order requried by the target QImage
298 MatColorOrder requiredOrder = getColorOrderOfRGB32Format();
299#if QT_VERSION >= 0x050200
300 if (formatHint == QImage::Format_RGBX8888
301 || formatHint == QImage::Format_RGBA8888
302 || formatHint == QImage::Format_RGBA8888_Premultiplied) {
303 requiredOrder = MCO_RGBA;
304 }
305#endif
306
307 if (order != requiredOrder)
308 mat_adjustCn = adjustChannelsOrder(mat, order, requiredOrder);
309 }
310
311 if (mat_adjustCn.empty())
312 mat_adjustCn = mat;
313
314 //Adjust mat depth if needed.
315 cv::Mat mat_adjustDepth = mat_adjustCn;
316 if (mat.depth() != CV_8U)
317 mat_adjustCn.convertTo(mat_adjustDepth, CV_8UC(mat_adjustCn.channels()), mat.depth() == CV_16U ? 1/255.0 : 255.0);
318
319 //Should we convert the image to the format specified by formatHint?
320 QImage image = mat2Image_shared(mat_adjustDepth, format);
321 if (format == formatHint || formatHint == QImage::Format_Invalid)
322 return image.copy();
323 else
324 return image.convertToFormat(formatHint);
325}
326
327/* Convert QImage to cv::Mat without data copy
328 */
329cv::Mat image2Mat_shared(const QImage &img, MatColorOrder *order)
330{
331 if (img.isNull())
332 return cv::Mat();
333
334 switch (img.format()) {
336 break;
337#if QT_VERSION >= 0x040400
339 if (order)
340 *order = MCO_RGB;
341 break;
342#endif
346 if (order)
347 *order = getColorOrderOfRGB32Format();
348 break;
349#if QT_VERSION >= 0x050200
353 if (order)
354 *order = MCO_RGBA;
355 break;
356#endif
357#if QT_VERSION >= 0x050500
360 break;
361#endif
362 default:
363 return cv::Mat();
364 }
365 return cv::Mat(img.height(), img.width(), CV_8UC(img.depth()/8), (uchar*)img.bits(), img.bytesPerLine());
366}
367
368/* Convert cv::Mat to QImage without data copy
369 */
370QImage mat2Image_shared(const cv::Mat &mat, QImage::Format formatHint)
371{
372 Q_ASSERT(mat.type() == CV_8UC1 || mat.type() == CV_8UC3 || mat.type() == CV_8UC4);
373
374 if (mat.empty())
375 return QImage();
376
377 //Adjust formatHint if needed.
378 if (mat.type() == CV_8UC1) {
379 if (formatHint != QImage::Format_Indexed8
380 #if QT_VERSION >= 0x050500
381 && formatHint != QImage::Format_Alpha8
382 && formatHint != QImage::Format_Grayscale8
383 #endif
384 ) {
385 formatHint = QImage::Format_Indexed8;
386 }
387#if QT_VERSION >= 0x040400
388 } else if (mat.type() == CV_8UC3) {
389 formatHint = QImage::Format_RGB888;
390#endif
391 } else if (mat.type() == CV_8UC4) {
392 if (formatHint != QImage::Format_RGB32
393 && formatHint != QImage::Format_ARGB32
395 #if QT_VERSION >= 0x050200
396 && formatHint != QImage::Format_RGBX8888
397 && formatHint != QImage::Format_RGBA8888
399 #endif
400 ) {
401 formatHint = QImage::Format_ARGB32;
402 }
403 }
404
405 QImage img(mat.data, mat.cols, mat.rows, mat.step, formatHint);
406
407 //Should we add directly support for user-customed-colorTable?
408 if (formatHint == QImage::Format_Indexed8) {
409 QVector<QRgb> colorTable;
410 for (int i=0; i<256; ++i)
411 colorTable.append(qRgb(i,i,i));
412 img.setColorTable(colorTable);
413 }
414 return img;
415}
416
417} //namespace QtOcv
uchar * bits()
qsizetype bytesPerLine() const const
QImage convertToFormat(Format format, Qt::ImageConversionFlags flags) &&
QImage copy(const QRect &rectangle) const const
int depth() const const
Format format() const const
int height() const const
bool isNull() const const
void setColorTable(const QList< QRgb > &colors)
int width() const const
void append(QList< T > &&value)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Apr 11 2025 11:57:09 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.