QtQtQt中各种图像的格式转换以及与cv::Mat图像格式之间的转换
OQS在使用 Qt 与 OpenCV 联合开发时,常常需要在 QImage 和 cv::Mat 之间进行图像格式转换。两者的图像格式不同,因此理解 QImage::Format 与 cv::Mat 类型的对应关系 非常关键。以下是常见格式的对应关系表:
| QImage::Format |
描述 |
对应的 cv::Mat 类型 |
通道顺序 |
QImage::Format_RGB888 |
24-bit RGB(每像素3字节) |
CV_8UC3 |
RGB → OpenCV中是BGR |
QImage::Format_BGR888 |
24-bit BGR(Qt 5.14+) |
CV_8UC3 |
BGR |
QImage::Format_ARGB32 |
32-bit ARGB(Alpha在高字节) |
CV_8UC4 |
BGRA(OpenCV里是BGRA) |
QImage::Format_ARGB32_Premultiplied |
预乘Alpha的32位ARGB |
CV_8UC4 |
BGRA(需额外处理Alpha) |
QImage::Format_RGB32 |
32-bit RGB(Alpha总为255) |
CV_8UC4 |
BGRA(Alpha恒为255) |
QImage::Format_Grayscale8 |
8-bit 灰度图(Qt 5.5+) |
CV_8UC1 |
灰度 |
QImage::Format_Indexed8 |
8-bit索引图(常见于灰度图) |
CV_8UC1 |
灰度 |
2. OpenCV图像底层
OpenCV 中的 cv::Mat 图像类型(type)是一个 整数类型常量(int),这个整数中编码了两个关键信息:
- 每个通道的数据类型(depth)
- 通道数(channels)
本质上的编码方式:
1
| type = (depth & 0x7) + ((channels - 1) << 3);
|
depth 和 channels 的取值
| 数据类型 |
depth 宏常量值 |
二进制值 |
含义 |
CV_8U |
0 |
000 |
8-bit unsigned char |
CV_8S |
1 |
001 |
8-bit signed char |
CV_16U |
2 |
010 |
16-bit unsigned short |
CV_16S |
3 |
011 |
16-bit signed short |
CV_32S |
4 |
100 |
32-bit signed int |
CV_32F |
5 |
101 |
32-bit float |
CV_64F |
6 |
110 |
64-bit double |
通道数的编码方式:
通道数编码在高位部分,每个通道是 (channels - 1) << 3:
| 通道数(C) |
(channels-1)<<3 |
最终编码中的通道部分 |
| 1 |
0 |
00000000 |
| 2 |
8 |
00001000 |
| 3 |
16 |
00010000 |
| 4 |
24 |
00011000 |
举例:CV_8UC3 是怎么计算出来的?
CV_8U 的值是 0
C3 表示通道数是 3 → (3-1)<<3 = 16
- 所以
CV_8UC3 = 0 + 16 = 16
cv::Mat::type() 返回的值就是这个整数(16)。
再举几个:
| 宏定义 |
值 |
分解 |
CV_8UC1 |
0 |
CV_8U + ((1-1)<<3) |
CV_8UC2 |
8 |
CV_8U + ((2-1)<<3) |
CV_8UC3 |
16 |
CV_8U + ((3-1)<<3) |
CV_8UC4 |
24 |
CV_8U + ((4-1)<<3) |
CV_16UC1 |
2 |
CV_16U + ((1-1)<<3) |
CV_16UC3 |
18 |
CV_16U + ((3-1)<<3) |
CV_32FC1 |
5 |
CV_32F + ((1-1)<<3) |
CV_32FC3 |
21 |
CV_32F + ((3-1)<<3) |
2.1 OpenCV 图像类型解释
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| ====================== OpenCV 图像类型解剖 ======================
类型格式: CV_<bit-depth>{U|S|F}C<channels> -------------------------------------------------------------- - CV : OpenCV 类型前缀 - bit-depth : 每个通道的位深(8/16/32/64) - U/S/F : U = unsigned(无符号整数) → uchar/ushort S = signed(有符号整数) → char/short/int F = float(浮点数) → float/double - C<channels>: 通道数(C1~C4)
====================== 常见类型举例 ========================
CV_8UC1 → 8位无符号灰度图 → uchar 灰度图 CV_8UC3 → 8位无符号彩色图(BGR)→ uchar 彩色图(BGR) CV_8UC4 → 8位无符号4通道(BGRA)→ uchar BGRA 图
CV_16UC1 → 16位无符号灰度图 → ushort 深度图、医学图像 CV_16SC1 → 16位有符号灰度图 → short 常用于梯度图、滤波 CV_32FC1 → 32位浮点灰度图 → float 图像处理、光流 CV_64FC4 → 64位浮点4通道图 → double 高频计算图
==================== 底层编码机制 =======================
OpenCV 图像类型实际是一个整数,编码方式如下:
type = (depth & 0x7) + ((channels - 1) << 3)
depth 类型编码: CV_8U = 0 → unsigned char CV_8S = 1 → char CV_16U = 2 → unsigned short CV_16S = 3 → short CV_32S = 4 → int CV_32F = 5 → float CV_64F = 6 → double
通道数编码:(channels - 1) << 3 C1 = 0 << 3 = 0 C2 = 1 << 3 = 8 C3 = 2 << 3 = 16 C4 = 3 << 3 = 24
例:CV_16UC3 = 2(depth) + (3-1)<<3 = 2 + 16 = 18
==========================================================
小技巧: int depth = CV_MAT_DEPTH(mat.type()); int channels = CV_MAT_CN(mat.type());
|
2.2 图像类型解剖图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| ┌──────────────┐ │ CV_16UC3 │ └─────┬────────┘ │ ┌────────▼────────────┐ │ CV_<Depth><C><N> │ │ │ │ Depth: 16U │ → unsigned short(16位无符号) │ C: C3 │ → 三通道(一般 BGR) └────────────────────┘
底层编码: depth = CV_16U = 2(0000 0010) channels = 3 → (3-1)<<3 = 16(0001 0000) → 最终类型 = 2 + 16 = 18
CV_16UC3 = 18
|
3. 图像格式转换
3.1cv::Mat 转 QImage
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| QImage Mat2QImage(const cv::Mat& mat) { switch (mat.type()) { case CV_8UC1: return QImage(mat.data, mat.cols, mat.rows, mat.step, QImage::Format_Grayscale8).copy(); case CV_8UC3: return QImage(mat.data, mat.cols, mat.rows, mat.step, QImage::Format_RGB888).rgbSwapped().copy(); case CV_8UC4: return QImage(mat.data, mat.cols, mat.rows, mat.step, QImage::Format_ARGB32).copy(); default: return QImage(); } }
|
3.2 QImage 转 cv::Mat
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| cv::Mat QImage2Mat(const QImage& image) { switch (image.format()) { case QImage::Format_RGB888: return cv::Mat(image.height(), image.width(), CV_8UC3, const_cast<uchar*>(image.bits()), image.bytesPerLine()).clone(); case QImage::Format_BGR888: return cv::Mat(image.height(), image.width(), CV_8UC3, const_cast<uchar*>(image.bits()), image.bytesPerLine()).clone(); case QImage::Format_ARGB32: case QImage::Format_RGB32: return cv::Mat(image.height(), image.width(), CV_8UC4, const_cast<uchar*>(image.bits()), image.bytesPerLine()).clone(); case QImage::Format_Grayscale8: case QImage::Format_Indexed8: return cv::Mat(image.height(), image.width(), CV_8UC1, const_cast<uchar*>(image.bits()), image.bytesPerLine()).clone(); case QImage::Format_Grayscale16: { return cv::Mat(image.height(), image.width(), CV_16UC1, const_cast<uchar*>(image.bits()), image.bytesPerLine()).clone(); } default: return cv::Mat(); } }
|
4. 注意事项:
QImage::Format_RGB888 和 OpenCV 的 CV_8UC3 通道顺序不同(OpenCV默认是 BGR),所以常需要 .rgbSwapped()。
QImage::Format_RGB32 实际也是 BGRA 格式,只不过 Alpha 通道为 255,OpenCV 也按 4 通道处理。
ARGB32_Premultiplied 通常要特别处理 Alpha 通道(不常用)。
- 为了避免内存共享带来的问题,建议
.copy() 或 .clone()。