|
6 | 6 |
|
7 | 7 | namespace OpenCVUtils { |
8 | 8 |
|
| 9 | +struct FormatEntry |
| 10 | +{ |
| 11 | + QImage::Format unifiedFormat = QImage::Format_Invalid; // 统一后的 Qt 格式 |
| 12 | + int openCvType = -1; // OpenCV 数据类型 |
| 13 | + int openCvCode = 0; // 颜色/通道转换码;0 表示无需转换 |
| 14 | +}; |
| 15 | + |
| 16 | +constexpr auto kMatToQTable = [] { |
| 17 | + std::array<std::array<FormatEntry, 5>, 4> table{}; |
| 18 | + |
| 19 | + auto set = [&](int depthIndex, int channelCount, QImage::Format unified, int code = 0) { |
| 20 | + table[depthIndex][channelCount] = {unified, -1, code}; |
| 21 | + }; |
| 22 | + |
| 23 | + /* 8-bit */ |
| 24 | + set(0, 1, QImage::Format_Grayscale8); |
| 25 | + set(0, 3, QImage::Format_BGR888); |
| 26 | + set(0, 4, QImage::Format_ARGB32, cv::COLOR_BGRA2RGBA); |
| 27 | + |
| 28 | + /* 16-bit */ |
| 29 | + set(1, 1, QImage::Format_Grayscale16); |
| 30 | + set(1, 3, QImage::Format_RGBX64); |
| 31 | + set(1, 4, QImage::Format_RGBA64, cv::COLOR_BGRA2RGBA); |
| 32 | + |
| 33 | + /* 32-bit float */ |
| 34 | + set(2, 3, QImage::Format_RGBX32FPx4); |
| 35 | + set(2, 4, QImage::Format_RGBA32FPx4, cv::COLOR_BGRA2RGBA); |
| 36 | + |
| 37 | + return table; |
| 38 | +}(); |
| 39 | + |
| 40 | +constexpr auto kQToMatTable = [] { |
| 41 | + constexpr int maxFormat = static_cast<int>(QImage::NImageFormats); |
| 42 | + std::array<FormatEntry, maxFormat> table{}; |
| 43 | + |
| 44 | + auto set = [&](QImage::Format from, QImage::Format unified, int type, int swap = 0) { |
| 45 | + table[from] = {unified, type, swap}; |
| 46 | + }; |
| 47 | + |
| 48 | + /* 8-bit 灰度 */ |
| 49 | + set(QImage::Format_Grayscale8, QImage::Format_Grayscale8, CV_8UC1); |
| 50 | + set(QImage::Format_Indexed8, QImage::Format_Grayscale8, CV_8UC1); // 降级为8位灰度 |
| 51 | + |
| 52 | + /* 16-bit 灰度 */ |
| 53 | + set(QImage::Format_Grayscale16, QImage::Format_Grayscale16, CV_16UC1); |
| 54 | + |
| 55 | + /* 8-bit 彩色 3 通道 */ |
| 56 | + set(QImage::Format_RGB888, QImage::Format_BGR888, CV_8UC3, cv::COLOR_RGB2BGR); |
| 57 | + set(QImage::Format_BGR888, QImage::Format_BGR888, CV_8UC3); |
| 58 | + set(QImage::Format_RGB32, QImage::Format_BGR888, CV_8UC3, cv::COLOR_RGB2BGR); |
| 59 | + |
| 60 | + /* 8-bit 彩色 4 通道 */ |
| 61 | + set(QImage::Format_ARGB32, QImage::Format_ARGB32, CV_8UC4, cv::COLOR_RGBA2BGRA); |
| 62 | + set(QImage::Format_ARGB32_Premultiplied, QImage::Format_ARGB32, CV_8UC4, cv::COLOR_RGBA2BGRA); |
| 63 | + set(QImage::Format_RGBA8888, QImage::Format_ARGB32, CV_8UC4, cv::COLOR_RGBA2BGRA); |
| 64 | + set(QImage::Format_RGBA8888_Premultiplied, QImage::Format_ARGB32, CV_8UC4, cv::COLOR_RGBA2BGRA); |
| 65 | + set(QImage::Format_RGBX8888, QImage::Format_BGR888, CV_8UC3, cv::COLOR_RGBA2BGR); |
| 66 | + |
| 67 | + /* 16-bit 彩色 */ |
| 68 | + set(QImage::Format_RGB16, QImage::Format_BGR888, CV_8UC3, cv::COLOR_BGR5652BGR); |
| 69 | + set(QImage::Format_RGB555, QImage::Format_BGR888, CV_8UC3, cv::COLOR_BGR5552BGR); |
| 70 | + set(QImage::Format_RGB666, QImage::Format_BGR888, CV_8UC3, cv::COLOR_RGB2BGR); |
| 71 | + |
| 72 | + /* 16/32-bit 浮点 & 16-bit 整数 */ |
| 73 | + set(QImage::Format_RGBX64, QImage::Format_RGBA64, CV_16UC3, cv::COLOR_RGB2BGR); |
| 74 | + set(QImage::Format_RGBA64, QImage::Format_RGBA64, CV_16UC4, cv::COLOR_RGBA2BGRA); |
| 75 | + set(QImage::Format_RGBA64_Premultiplied, QImage::Format_RGBA64, CV_16UC4, cv::COLOR_RGBA2BGRA); |
| 76 | + |
| 77 | + set(QImage::Format_RGBX16FPx4, QImage::Format_RGBX16FPx4, CV_16FC3, cv::COLOR_RGB2BGR); |
| 78 | + set(QImage::Format_RGBA16FPx4, QImage::Format_RGBA16FPx4, CV_16FC4, cv::COLOR_RGBA2BGRA); |
| 79 | + set(QImage::Format_RGBA16FPx4_Premultiplied, |
| 80 | + QImage::Format_RGBA16FPx4, |
| 81 | + CV_16FC4, |
| 82 | + cv::COLOR_RGBA2BGRA); |
| 83 | + |
| 84 | + set(QImage::Format_RGBX32FPx4, QImage::Format_RGBX32FPx4, CV_32FC3, cv::COLOR_RGB2BGR); |
| 85 | + set(QImage::Format_RGBA32FPx4, QImage::Format_RGBA32FPx4, CV_32FC4, cv::COLOR_RGBA2BGRA); |
| 86 | + set(QImage::Format_RGBA32FPx4_Premultiplied, |
| 87 | + QImage::Format_RGBA32FPx4, |
| 88 | + CV_32FC4, |
| 89 | + cv::COLOR_RGBA2BGRA); |
| 90 | + |
| 91 | + /* 其余格式置 Invalid,调用端可直接返回空 */ |
| 92 | + return table; |
| 93 | +}(); |
| 94 | + |
9 | 95 | auto qImageToMat(const QImage &qimage) -> cv::Mat |
10 | 96 | { |
11 | | - cv::Mat mat; |
12 | | - |
13 | | - switch (qimage.format()) { |
14 | | - case QImage::Format_Grayscale8: |
15 | | - mat = cv::Mat(qimage.height(), |
16 | | - qimage.width(), |
17 | | - CV_8UC1, |
18 | | - (void *) qimage.constBits(), |
19 | | - qimage.bytesPerLine()) |
20 | | - .clone(); |
21 | | - break; |
22 | | - case QImage::Format_Grayscale16: |
23 | | - mat = cv::Mat(qimage.height(), |
24 | | - qimage.width(), |
25 | | - CV_16UC1, |
26 | | - (void *) qimage.constBits(), |
27 | | - qimage.bytesPerLine()) |
28 | | - .clone(); |
29 | | - break; |
30 | | - case QImage::Format_Indexed8: |
31 | | - mat = qImageToMat(qimage.convertToFormat(QImage::Format_RGB888)); |
32 | | - break; |
33 | | - case QImage::Format_RGB888: |
34 | | - mat = cv::Mat(qimage.height(), |
35 | | - qimage.width(), |
36 | | - CV_8UC3, |
37 | | - (void *) qimage.constBits(), |
38 | | - qimage.bytesPerLine()) |
39 | | - .clone(); |
40 | | - cv::cvtColor(mat, mat, cv::COLOR_RGB2BGR); |
41 | | - break; |
42 | | - case QImage::Format_RGB32: |
43 | | - case QImage::Format_ARGB32: |
44 | | - case QImage::Format_ARGB32_Premultiplied: |
45 | | - case QImage::Format_RGBA8888: |
46 | | - case QImage::Format_RGBA8888_Premultiplied: |
47 | | - case QImage::Format_RGBX64: |
48 | | - case QImage::Format_RGBA64: { |
49 | | - auto rgb888 = qimage.convertToFormat(QImage::Format_RGB888); |
50 | | - mat = cv::Mat(rgb888.height(), |
51 | | - rgb888.width(), |
52 | | - CV_8UC3, |
53 | | - (void *) rgb888.constBits(), |
54 | | - rgb888.bytesPerLine()) |
55 | | - .clone(); |
56 | | - cv::cvtColor(mat, mat, cv::COLOR_RGB2BGR); |
57 | | - break; |
| 97 | + if (qimage.isNull()) { |
| 98 | + return {}; |
| 99 | + } |
| 100 | + |
| 101 | + const int formatValue = qimage.format(); |
| 102 | + if (formatValue < 0 || formatValue >= int(std::size(kQToMatTable))) { |
| 103 | + return {}; |
| 104 | + } |
| 105 | + |
| 106 | + const auto [unifiedFormat, openCvType, openCvCode] = kQToMatTable[formatValue]; |
| 107 | + if (unifiedFormat == QImage::Format_Invalid) { |
| 108 | + return {}; |
58 | 109 | } |
59 | | - default: qWarning() << "Unsupported QImage format:" << qimage.format(); break; |
| 110 | + |
| 111 | + const auto unifiedImage = qimage.convertToFormat(unifiedFormat); |
| 112 | + |
| 113 | + cv::Mat rawMat(unifiedImage.height(), |
| 114 | + unifiedImage.width(), |
| 115 | + openCvType, |
| 116 | + const_cast<uchar *>(unifiedImage.bits()), |
| 117 | + unifiedImage.bytesPerLine()); |
| 118 | + cv::Mat result = rawMat.clone(); // 脱离 Qt 内存 |
| 119 | + if (openCvCode) { |
| 120 | + cv::cvtColor(result, result, openCvCode); |
60 | 121 | } |
61 | 122 |
|
62 | | - return mat; |
| 123 | + return result; |
63 | 124 | } |
64 | 125 |
|
65 | 126 | auto matToQImage(const cv::Mat &mat) -> QImage |
66 | 127 | { |
67 | | - QImage qimage; |
68 | | - |
69 | | - switch (mat.type()) { |
70 | | - case CV_8UC1: |
71 | | - qimage = QImage(mat.data, mat.cols, mat.rows, mat.step, QImage::Format_Grayscale8).copy(); |
72 | | - break; |
73 | | - case CV_8UC3: { |
74 | | - cv::Mat rgb; |
75 | | - cv::cvtColor(mat, rgb, cv::COLOR_BGR2RGB); |
76 | | - qimage = QImage(rgb.data, rgb.cols, rgb.rows, rgb.step, QImage::Format_RGB888).copy(); |
77 | | - break; |
| 128 | + if (mat.empty()) { |
| 129 | + return {}; |
78 | 130 | } |
79 | | - case CV_8UC4: { |
80 | | - cv::Mat rgba; |
81 | | - cv::cvtColor(mat, rgba, cv::COLOR_BGRA2RGBA); |
82 | | - qimage = QImage(rgba.data, rgba.cols, rgba.rows, rgba.step, QImage::Format_RGBA8888).copy(); |
83 | | - break; |
| 131 | + |
| 132 | + int depthIndex = -1; |
| 133 | + switch (mat.depth()) { |
| 134 | + case CV_8U: depthIndex = 0; break; |
| 135 | + case CV_16U: depthIndex = 1; break; |
| 136 | + case CV_32F: depthIndex = 2; break; |
| 137 | + default: depthIndex = -1; break; |
84 | 138 | } |
85 | | - case CV_16UC1: |
86 | | - qimage = QImage(mat.data, mat.cols, mat.rows, mat.step, QImage::Format_Grayscale16).copy(); |
87 | | - break; |
88 | | - case CV_32FC1: |
89 | | - case CV_32FC3: |
90 | | - case CV_32FC4: { |
91 | | - cv::Mat normalized; |
92 | | - cv::normalize(mat, normalized, 0, 255, cv::NORM_MINMAX, CV_8U); |
93 | | - qimage = matToQImage(normalized); |
94 | | - break; |
| 139 | + const int channelCount = mat.channels(); |
| 140 | + if (depthIndex < 0 || channelCount < 1 || channelCount > 4) { |
| 141 | + return {}; |
95 | 142 | } |
96 | | - default: qWarning() << "Unsupported cv::Mat type:" << mat.type(); break; |
| 143 | + |
| 144 | + const auto [unifiedFormat, openCvType, openCvCode] = kMatToQTable[depthIndex][channelCount]; |
| 145 | + if (unifiedFormat == QImage::Format_Invalid) { |
| 146 | + return {}; |
| 147 | + } |
| 148 | + |
| 149 | + cv::Mat convertedMat; |
| 150 | + if (openCvCode) { |
| 151 | + cv::cvtColor(mat, convertedMat, openCvCode); |
| 152 | + } else { |
| 153 | + convertedMat = mat; |
97 | 154 | } |
98 | 155 |
|
99 | | - return qimage; |
| 156 | + return QImage(convertedMat.data, |
| 157 | + convertedMat.cols, |
| 158 | + convertedMat.rows, |
| 159 | + convertedMat.step, |
| 160 | + unifiedFormat) |
| 161 | + .copy(); |
100 | 162 | } |
101 | 163 |
|
102 | 164 | } // namespace OpenCVUtils |
0 commit comments