You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 13:03:20