Skip to content

Commit a875751

Browse files
committed
[opencv]: 增强直方图均衡化算法并重构图像格式转换模块
- 直方图均衡化: 扩展支持多通道和多种深度图像处理,包括8位/16位/浮点类型的单通道和彩色图像 - 图像格式转换: 重构QImage与cv::Mat转换逻辑,使用查表法统一处理多种格式转换 - 性能优化: 改进颜色空间转换流程,增加深度标准化处理,提升处理精度和兼容性 - 代码结构: 使用常量表达式表格替代多重条件判断,提高代码可维护性和扩展性
1 parent d0a8195 commit a875751

2 files changed

Lines changed: 193 additions & 86 deletions

File tree

src/qopencv/enhancement/histogramequalization.cc

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,61 @@ auto HistogramEqualization::apply(const cv::Mat &src) -> cv::Mat
1111
return Utils::asynchronous<cv::Mat>([src]() -> cv::Mat {
1212
cv::Mat dst;
1313
try {
14-
if (src.channels() == 3) {
15-
cv::Mat yuv;
16-
cv::cvtColor(src, yuv, cv::COLOR_BGR2YUV);
17-
std::vector<cv::Mat> yuv_channels;
18-
cv::split(yuv, yuv_channels);
19-
cv::equalizeHist(yuv_channels[0], yuv_channels[0]);
20-
cv::merge(yuv_channels, yuv);
21-
cv::cvtColor(yuv, dst, cv::COLOR_YUV2BGR);
14+
cv::Mat gray8;
15+
const int depth = src.depth(); // CV_8U / CV_16U / CV_32F ...
16+
const int ch = src.channels();
17+
18+
if (ch == 1 && depth == CV_8U) {
19+
gray8 = src;
2220
} else {
23-
cv::equalizeHist(src, dst);
21+
cv::Mat tmp;
22+
if (ch > 1) {
23+
cv::cvtColor(src, tmp, cv::COLOR_BGR2GRAY);
24+
} else {
25+
tmp = src;
26+
}
27+
28+
if (depth == CV_8U) {
29+
gray8 = tmp;
30+
} else if (depth == CV_16U) {
31+
tmp.convertTo(gray8, CV_8U, 255.0 / 65535.0);
32+
} else if (depth == CV_32F || depth == CV_64F) {
33+
double minVal, maxVal;
34+
cv::minMaxLoc(tmp, &minVal, &maxVal);
35+
if (maxVal > minVal)
36+
tmp.convertTo(gray8,
37+
CV_8U,
38+
255.0 / (maxVal - minVal),
39+
-255.0 * minVal / (maxVal - minVal));
40+
else
41+
tmp.convertTo(gray8, CV_8U, 0);
42+
} else {
43+
cv::normalize(tmp, gray8, 0, 255, cv::NORM_MINMAX, CV_8U);
44+
}
45+
}
46+
47+
cv::equalizeHist(gray8, gray8);
48+
49+
if (ch == 1 && depth == CV_8U) {
50+
dst = gray8;
51+
} else {
52+
cv::Mat grayOriginalDepth;
53+
if (depth == CV_8U) {
54+
grayOriginalDepth = gray8;
55+
} else if (depth == CV_16U) {
56+
gray8.convertTo(grayOriginalDepth, CV_16U, 65535.0 / 255.0);
57+
} else if (depth == CV_32F || depth == CV_64F) {
58+
gray8.convertTo(grayOriginalDepth, depth, 1.0 / 255.0);
59+
} else {
60+
grayOriginalDepth = gray8.clone();
61+
}
62+
63+
if (ch > 1) {
64+
std::vector<cv::Mat> planes(ch, grayOriginalDepth);
65+
cv::merge(planes, dst);
66+
} else {
67+
dst = grayOriginalDepth;
68+
}
2469
}
2570
} catch (const cv::Exception &e) {
2671
qWarning() << "HistogramEqualization:" << e.what();

src/qopencv/opencvutils.cc

Lines changed: 139 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -6,97 +6,159 @@
66

77
namespace OpenCVUtils {
88

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+
995
auto qImageToMat(const QImage &qimage) -> cv::Mat
1096
{
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 {};
58109
}
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);
60121
}
61122

62-
return mat;
123+
return result;
63124
}
64125

65126
auto matToQImage(const cv::Mat &mat) -> QImage
66127
{
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 {};
78130
}
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;
84138
}
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 {};
95142
}
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;
97154
}
98155

99-
return qimage;
156+
return QImage(convertedMat.data,
157+
convertedMat.cols,
158+
convertedMat.rows,
159+
convertedMat.step,
160+
unifiedFormat)
161+
.copy();
100162
}
101163

102164
} // namespace OpenCVUtils

0 commit comments

Comments
 (0)