如何让QImage输出匹配cv::dnn::blobFromImage的数据格式?
用QImage替代OpenCV生成YOLO模型输入张量
问题概述
原项目使用OpenCV的cv::dnn::blobFromImage生成YOLO模型所需的输入张量,但为了减少依赖,尝试用Qt的QImage实现相同功能,却发现生成的张量数据格式与OpenCV输出不一致,导致模型无法正常工作。
原OpenCV实现代码
cv::Mat image = imread(image_path, cv::IMREAD_COLOR); cv::Mat blob = cv::dnn::blobFromImage( image, 1 / 255.0, cv::Size(640, 640), cv::Scalar(0, 0, 0), false, false ); float* input_tensor_values = (float*)blob.data; process_image(input_tensor_values);
尝试的QImage实现(存在格式问题)
QImage img(image_path); img = img.scaled(640, 640, Qt::AspectRatioMode::KeepAspectRatioByExpanding, Qt::SmoothTransformation); img = img.copy(0, 0, 640, 640); img = img.convertToFormat(QImage::Format_BGR888); // 也曾尝试RGB24 // 若加载640x640图像,此时img.bits()与cv::Mat image像素值一致 // 进行缩放: auto src = img.constBits(); float* input_tensor_values = /* 存储区域 */; for (int i = 0; i < 3 * 640 * 640;) { input_tensor_values[i + 0] = (float(src[0]) / 255.f); input_tensor_values[i + 1] = (float(src[1]) / 255.f); input_tensor_values[i + 2] = (float(src[2]) / 255.f); // clang-format on i += 3; src += 3; } process_image(input_tensor_values);
差异原因
OpenCV的blobFromImage生成的是CHW格式(通道-高度-宽度)的张量:所有B通道像素连续存储,接着是G通道,最后是R通道(因原代码swapRB=false,保持OpenCV默认的BGR顺序)。而上述QImage代码是按HWC格式(高度-宽度-通道)存储,即每个像素的B、G、R值连续排列,两种格式的数据排布完全不同,导致张量不匹配。
此外,原QImage的缩放方式(保持比例后裁剪)与OpenCV直接缩放到目标尺寸的行为不一致,也是数据差异的一个来源。
修正后的QImage实现
QImage img(image_path); // 与OpenCV行为对齐:直接缩放到目标尺寸,忽略原比例 img = img.scaled(640, 640, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); // 转换为BGR888格式,匹配OpenCV的IMREAD_COLOR通道顺序 img = img.convertToFormat(QImage::Format_BGR888); const int width = img.width(); const int height = img.height(); const int channel_count = 3; // 分配张量内存:3通道 × 640 × 640 float* input_tensor_values = new float[channel_count * width * height]; // 将HWC格式转换为CHW格式,同时完成归一化 for (int channel = 0; channel < channel_count; ++channel) { for (int y = 0; y < height; ++y) { // 获取当前行的起始指针(处理QImage内存对齐问题) const uchar* row_ptr = img.scanLine(y); for (int x = 0; x < width; ++x) { // 提取对应通道的像素值:B(0), G(1), R(2) uchar pixel_val = row_ptr[x * 3 + channel]; // 计算张量中的索引:通道优先 int tensor_idx = channel * width * height + y * width + x; input_tensor_values[tensor_idx] = static_cast<float>(pixel_val) / 255.0f; } } } // 传入模型处理 process_image(input_tensor_values); // 释放内存 delete[] input_tensor_values;
关键调整点
- 缩放方式改为
Qt::IgnoreAspectRatio,与OpenCV直接resize到目标尺寸的逻辑一致 - 使用
scanLine读取每行像素,避免QImage内存对齐导致的错误 - 按CHW格式重新排布数据:先存储完整的B通道,再G、R通道
- 保持BGR通道顺序,与原OpenCV代码的
swapRB=false设置匹配
内容的提问来源于stack exchange,提问作者Jean-Michaël Celerier
相关产品推荐
相关产品推荐

