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

如何用C++结合ONNX与OpenCV实现图像超分辨率?(Python转C++出错求助)

解决AnimeSR ONNX模型C++版本预处理/后处理问题

参考Python实现(可正常运行)

session = onnxruntime.InferenceSession('animesr.onnx')

img = cv2.imread('imgs/naruto.jpg')
ori_h, ori_w, _ = img.shape
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = cv2.resize(img, (512,512))
img = (np.array(img) / 255.0).astype(np.float32)
img = np.transpose(img, (2, 0, 1))
img = np.expand_dims(img, 0)

input_name = session.get_inputs()[0].name
output_name = session.get_outputs()[0].name

input_feed = {input_name: img}
output = session.run([output_name], input_feed)

output = output[0].clip(0, 1) * 255
output = output.astype(np.uint8)
output = np.squeeze(output)
output = np.transpose(output, (1, 2, 0))
output = cv2.cvtColor(output, cv2.COLOR_RGB2BGR)
output = cv2.resize(output, (ori_w*4, ori_h*4))
cv2.imwrite('naruto_animesr.jpg', output)

正确的C++实现代码

Ort::Session session(env, ORT_TSTR(modelPath), sessionOptions);

cv::Mat inputImage = cv::imread(imagePath, cv::IMREAD_COLOR);
if (inputImage.empty()) {
    // 处理图像读取失败逻辑
    return;
}
int ori_h = inputImage.rows;
int ori_w = inputImage.cols;

// ---------------------- 预处理环节 ----------------------
cv::Mat resizedImage;
cv::resize(inputImage, resizedImage, cv::Size(512, 512));
// BGR转RGB
cv::Mat rgbImage;
cv::cvtColor(resizedImage, rgbImage, cv::COLOR_BGR2RGB);

// 转换为float并归一化
cv::Mat floatImage;
rgbImage.convertTo(floatImage, CV_32FC3, 1.0 / 255.0);

// 将HWC布局转为CHW布局
std::vector<float> inputData(3 * 512 * 512);
int idx = 0;
for (int c = 0; c < 3; ++c) {
    for (int h = 0; h < 512; ++h) {
        for (int w = 0; w < 512; ++w) {
            inputData[idx++] = floatImage.at<cv::Vec3f>(h, w)[c];
        }
    }
}

// 创建ONNX输入张量
Ort::MemoryInfo memoryInfo = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
std::vector<int64_t> inputShape = {1, 3, 512, 512};
Ort::Value inputTensor = Ort::Value::CreateTensor<float>(memoryInfo, inputData.data(), inputData.size(), inputShape.data(), inputShape.size());

// 动态获取输入输出名称(避免硬编码)
Ort::AllocatorWithDefaultOptions allocator;
const char* inputName = session.GetInputName(0, allocator);
const char* outputName = session.GetOutputName(0, allocator);
std::vector<const char*> inputNames = {inputName};
std::vector<const char*> outputNames = {outputName};

// 运行模型
std::vector<Ort::Value> outputTensor = session.Run(Ort::RunOptions{}, inputNames.data(), &inputTensor, 1, outputNames.data(), 1);

// ---------------------- 后处理环节 ----------------------
Ort::TensorTypeAndShapeInfo outputInfo = outputTensor[0].GetTensorTypeAndShapeInfo();
std::vector<int64_t> outputShape = outputInfo.GetShape();
int channels = outputShape[1]; // 3
int height = outputShape[2]; // 2048
int width = outputShape[3]; // 2048

const float* outputData = outputTensor[0].GetTensorData<float>();

// 将CHW布局转为HWC的RGB图像,同时完成clip(0,1)操作
cv::Mat outputImage(height, width, CV_32FC3);
idx = 0;
for (int c = 0; c < 3; ++c) {
    for (int h = 0; h < height; ++h) {
        for (int w = 0; w < width; ++w) {
            float val = std::max(0.0f, std::min(1.0f, outputData[idx++]));
            outputImage.at<cv::Vec3f>(h, w)[c] = val * 255.0f;
        }
    }
}

// 转换为uint8格式,RGB转BGR
cv::Mat uint8Image;
outputImage.convertTo(uint8Image, CV_8UC3);
cv::Mat bgrOutput;
cv::cvtColor(uint8Image, bgrOutput, cv::COLOR_RGB2BGR);

// 缩放回原图的4倍大小
cv::Mat finalOutput;
cv::resize(bgrOutput, finalOutput, cv::Size(ori_w * 4, ori_h * 4));

// 保存结果
cv::imwrite(outputPath, finalOutput);

关键修正点

  • 输入布局转换:OpenCV的Mat默认是HWC(高、宽、通道)的BGR顺序,而模型需要CHW(通道、高、宽)的RGB顺序,必须手动遍历转置通道,不能直接用floatImage.data作为张量输入,否则会导致通道顺序混乱。
  • 输出clip操作:补全Python代码中clip(0,1)的逻辑,避免像素值溢出导致的异常色彩。
  • 输出布局转换:模型输出是CHW布局,需要转置回HWC后再转换为BGR格式,最后缩放至原图4倍尺寸,和Python逻辑完全对齐。
  • 动态名称获取:通过session.GetInputName动态获取输入输出名称,避免硬编码名称导致的模型兼容性问题。

内容的提问来源于stack exchange,提问作者Rizeky

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 03:51:02